You could try by()
in base R. For example,
tab <- with(df1, by(df1, list(Method = Method, Type = Type), FUN = length))
Method: A
Type: Fast
[1] 2
------------------------------------------------------------
Method: B
Type: Fast
[1] 1
------------------------------------------------------------
Method: C
Type: Fast
[1] 1
------------------------------------------------------------
Method: A
Type: Medium
[1] 1
------------------------------------------------------------
Method: B
Type: Medium
[1] NA
------------------------------------------------------------
Method: C
Type: Medium
[1] 1
------------------------------------------------------------
Method: A
Type: Slow
[1] NA
------------------------------------------------------------
....
Note that that is just the print()
method making it look complicated. If we unclass()
tab
, we see it is just a multi-way table in this instance:
R> unclass(tab)
Type
Method Fast Medium Slow
A 2 1 NA
B 1 NA 1
C 1 1 2
attr(,"call")
by.data.frame(data = df1, INDICES = list(Method = Method, Type = Type),
FUN = nrow)
and you can work with that as it is just an array (a matrix). And if you prefer this in the long format, you can easily unwind it:
nr <- nrow(tab)
ltab <- cbind.data.frame(Method = rep(rownames(tab), times = nr),
Type = rep(colnames(tab), each = nr),
Count = c(tab))
ltab
R> ltab
Method Type Count
1 A Fast 2
2 B Fast 1
3 C Fast 1
4 A Medium 1
5 B Medium NA
6 C Medium 1
7 A Slow NA
8 B Slow 1
9 C Slow 2