We could split the data by 'Group' and use xtabs
lapply(split(df1[-1], df1$Group), function(x)
{
lvls <- sort(unique(c(unlist(x[1:2]), "AC")))
x[1:2] <- lapply(x[1:2], factor, levels = lvls)
xtabs(Count ~ Actor + Receiver, data = x)
})
-output
$A
Receiver
Actor AA AB AC AH
AA 0 3 0 6
AB 0 0 0 3
AC 0 0 0 0
AH 0 0 0 0
If we need to convert back
lst1 <- lapply(split(df1[-1], df1$Group), function(x)
{
lvls <- sort(unique(c(unlist(x[1:2]), "AC")))
x[1:2] <- lapply(x[1:2], factor, levels = lvls)
as.data.frame(xtabs(Count ~ Actor + Receiver, data = x))
})
out <- do.call(rbind, Map(cbind, Group = names(lst1), lst1))
row.names(out) <- NULL
-output
> out
Group Actor Receiver Freq
1 A AA AA 0
2 A AB AA 0
3 A AC AA 0
4 A AH AA 0
5 A AA AB 3
6 A AB AB 0
7 A AC AB 0
8 A AH AB 0
9 A AA AC 0
10 A AB AC 0
11 A AC AC 0
12 A AH AC 0
13 A AA AH 6
14 A AB AH 3
15 A AC AH 0
16 A AH AH 0
Or may also convert to a 3D array with xtabs and reconvert with as.data.frame
lvls <- sort(unique(unlist(df1[2:3])))
as.data.frame(xtabs(Count ~ Actor + Receiver + Group,
transform(df1, Actor = factor(Actor, levels = lvls),
Receiver = factor(Receiver, levels = lvls))))
We may be able to expand the data without having to reshape and then reconvert
library(dplyr)
library(tidyr)
df1 %>%
group_by(Group) %>%
complete(Actor = sort(unique(c(Actor, Receiver, "AC"))),
Receiver = sort(unique(c(Actor, Receiver, "AC"))),
fill = list(Count = 0)) %>%
ungroup
-output
# A tibble: 16 × 4
Group Actor Receiver Count
<chr> <chr> <chr> <int>
1 A AA AA 0
2 A AA AB 3
3 A AA AC 0
4 A AA AH 6
5 A AB AA 0
6 A AB AB 0
7 A AB AC 0
8 A AB AH 3
9 A AC AA 0
10 A AC AB 0
11 A AC AC 0
12 A AC AH 0
13 A AH AA 0
14 A AH AB 0
15 A AH AC 0
16 A AH AH 0
data
df1 <- structure(list(Group = c("A", "A", "A"), Actor = c("AA", "AA",
"AB"), Receiver = c("AB", "AH", "AH"), Count = c(3L, 6L, 3L)),
class = "data.frame", row.names = c(NA,
-3L))