Home > Software design >  List of matrices from dataframe
List of matrices from dataframe

Time:09-04

I want to convert a data frame into a list matrices for each group within my data set (each group has different number of individuals). The column containing actor names should be the rows and the receiver columns should be the columns, in the matrix. Lastly, the matrix should be filled with the numbers from another columns. The data frame looks something like this -

Group   Actor Receiver Count
  A      AA      AB        3
  A      AA      AH        6
  A      AB      AH        3

...

And I would like to convert it to

[[A]]
           [,AA] [,AB] [,AC] [,AH]
    [AA,]    0      3     0     6
    [AB,]    0      0     0     3
    [AC,]    0      0     0     0
    [AH,]    0      0     0     0

followed by matrices for other groups.

CodePudding user response:

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))
  • Related