Как разделить столбцы в списке на разные значения, хранящиеся в другом списке? - PullRequest
0 голосов
/ 08 ноября 2019

У меня есть список фреймов данных, и я пытаюсь нормализовать данные в нескольких столбцах путем деления каждой строки в столбце на сумму. Проблема в том, что все суммы разные.

Я начал с суммирования каждого столбца в переменной, используя lapply, что привело к появлению списка столбцов. Однако я не уверен, как разделить каждый список по соответствующим значениям.

Вот пример использования некоторого кода, который я составил

df1=data.frame("total"=c(50,100,75),
               "a"=c(15,50,30),
               "b"=c(15,10,5),
               "c"=c(20,40,40))

df2=data.frame("total"=c(100,200,400,100),
               "a"=c(10,40,100,50),
               "b"=c(50,100,200,30),
               "c"=c(40,60,100,20))

df3=data.frame("total"=c(40,60,80),
               "a"=c(15,30,50),
               "b"=c(25,20,20),
               "c"=c(0,10,10))
listex=list(df1=df1,df2=df2,df3=df3)
listtotal=lapply(listex,function(x) {x=colSums(x,na.rm=T)})

listex
$df1
  total  a  b  c
1    50 15 15 20
2   100 50 10 40
3    75 30  5 40

$df2
  total   a   b   c
1   100  10  50  40
2   200  40 100  60
3   400 100 200 100
4   100  50  30  20

$df3
  total  a  b  c
1    40 15 25  0
2    60 30 20 10
3    80 50 20 10

listtotal
$df1
total     a     b     c 
  225    95    30   100 

$df2
total     a     b     c 
  800   200   380   220 

$df3
total     a     b     c 
  180    95    65    20 

То, что я хочу получить, заключается в следующем, но, вы знаете, без необходимости писать все заново

df1n=data.frame("total"=c(50/225,100/225,75/225),"a"=c(15/95,50/95,30/95),
                "b"=c(15/30,10/30,5/30),"c"=c(20/100,40/100,40/100))
df2n=data.frame("total"=c(100/800,200/800,400/800,100/800),
                "a"=c(10/200,40/200,100/200,50/200),
                "b"=c(50/380,100/380,200/380,30/380),
                "c"=c(40/220,60/220,100/220,20/220))
df3n=data.frame('total'=c(40/180,60/180,80/180),
                'a'=c(15/95,30/95,50/95),
                'b'=c(25/65,20/65,20/65),
                'c'=c(0/20,10/20,10/20))

listn=list(df1=df1n,df2=df2n,df3=df3n)
listn
$df1
      total         a         b   c
1 0.2222222 0.1578947 0.5000000 0.2
2 0.4444444 0.5263158 0.3333333 0.4
3 0.3333333 0.3157895 0.1666667 0.4

$df2
  total    a          b          c
1 0.125 0.05 0.13157895 0.18181818
2 0.250 0.20 0.26315789 0.27272727
3 0.500 0.50 0.52631579 0.45454545
4 0.125 0.25 0.07894737 0.09090909

$df3
      total         a         b   c
1 0.2222222 0.1578947 0.3846154 0.0
2 0.3333333 0.3157895 0.3076923 0.5
3 0.4444444 0.5263158 0.3076923 0.5

Я думаю, что это как-то связано с вложенной функцией apply, но я не уверен, как именно я это сделаю. Любая помощь очень ценится!

Ответы [ 2 ]

2 голосов
/ 08 ноября 2019

Мы можем сделать арифметику на равных по длине векторах / матрицах / data.frames. Один из вариантов - разделить каждый из элементов в list на реплицированный colSums, чтобы сделать размеры одинаковыми

lapply(listex, function(x) x/colSums(x)[col(x)])

Кроме того, если listtotal - это другой объект, используйте Map разделить соответствующие элементы одного объекта с другим

Map(function(x, y) x/y[col(x)], listex, listtotal)
1 голос
/ 08 ноября 2019

Вы можете использовать функцию sweep

lapply(listex, function(x) sweep(x, 2, colSums(x), '/'))

Или преобразовать списки в список, чтобы использовать / напрямую

lapply(listex, function(x) x/as.list(colSums(x)))

Вывод для обоих методов:

# $`df1`
#       total         a         b   c
# 1 0.2222222 0.1578947 0.5000000 0.2
# 2 0.4444444 0.5263158 0.3333333 0.4
# 3 0.3333333 0.3157895 0.1666667 0.4
# 
# $df2
#   total    a          b          c
# 1 0.125 0.05 0.13157895 0.18181818
# 2 0.250 0.20 0.26315789 0.27272727
# 3 0.500 0.50 0.52631579 0.45454545
# 4 0.125 0.25 0.07894737 0.09090909
# 
# $df3
#       total         a         b   c
# 1 0.2222222 0.1578947 0.3846154 0.0
# 2 0.3333333 0.3157895 0.3076923 0.5
# 3 0.4444444 0.5263158 0.3076923 0.5
...