将变量组合到一个列表中

jac*_*tra 7 variables r list dataframe

伙计们,

我很难接受以下挑战.我有一个如下所示的数据集:

BuyerID    Fruit.1     Fruit.2    Fruit.3    Amount.1    Amount.2    Amount.3
879        Banana      Apple                 4           3
765        Strawberry  Apple      Orange     1           2           4
123        Orange      Banana                1           1           1
 11        Strawberry                        3
773        Kiwi        Banana                1           2
Run Code Online (Sandbox Code Playgroud)

我想做的是简化数据(如果可能)并折叠"Fruit"和"Amount"变量

BuyerID    Fruit                             Amount      Total    Count
879        "Banana" "Apple"                  4  3            7        2
765        "Strawberry" "Apple" "Orange"     1  2  4         7        3
123        "Orange" "Banana"                 1  1  1         3        2
 11        "Strawberry"                      3               3        1
773        "Kiwi" "Banana"                   1  2            3        2
Run Code Online (Sandbox Code Playgroud)

我已经尝试过使用c()和rbind(),但是它们没有产生我想要的结果 - 我在这里尝试了一些提示:data.frame行也是一个列表但是我不太确定这是否是最好的方法简化我的数据.

这可能是因为我可能更容易处理较少的变量来计算某些项目的出现(例如60%的买家购买香蕉).

我希望这是可行的 - 我也对任何建议持开放态度.任何解决方案升值

谢谢.

mne*_*nel 11

尝试复制数据并使用data.table

DT  <- data.frame(
  BuyerID = c(879,765,123,11,773), 
  Fruit.1 = c('Banana','Strawberry','Orange','Strawberry','Kiwi'),
  Fruit.2 = c('Apple','Apple','Banana',NA,'Banana'),
  Fruit.3 = c( NA, 'Orange',NA,NA,NA),
  Amount.1 = c(4,1,1,3,1), Amount.2 = c(3,2,1,NA,2), Amount.3 = c(NA,4,1,NA,NA),
  Total = c(7,7,3,3,3), 
  Count = c(2,3,2,1,2), 
  stringsAsFactors = FALSE)

# reshaping to long form and data.table

library(data.table)
DTlong <- data.table(reshape(DT, varying = list(Fruit = 2:4, Amount = 5:7), 
  direction = 'long'))

# create lists (without NA values)
# also adding count and total columns 
# by using <- to save Fruit and Amount for later use

DTlist <- DTlong[, list(Fruit <- list(as.vector(na.omit(Fruit.1))), 
                        Amount <- list(as.vector(na.omit(Amount.1))), 
                        Count  = length(unlist(Fruit)),
                        Total = sum(unlist(Amount))), 
                 by = BuyerID]

  BuyerID                      V1    V2 Count Total
1:     879            Banana,Apple   4,3     2     7
2:     765 Strawberry,Apple,Orange 1,2,4     3     7
3:     123           Orange,Banana 1,1,1     2     3
4:      11              Strawberry     3     1     3
5:     773             Kiwi,Banana   1,2     2     3
Run Code Online (Sandbox Code Playgroud)

@RicardoSaporta编辑:

您可以跳过重塑步骤,如果您愿意,使用list(list(c(....)))
这可能会节省相当多的执行时间(缺点是它不添加NA空格).但是,正如@Marius指出的那样,DTlong上面的内容可能更容易使用.

DT <- data.table(DT)
DT[,   Fruit := list(list(c(  Fruit.1,   Fruit.2,   Fruit.3))), by=BuyerID]
DT[, Ammount := list(list(c(Amount.1, Amount.2, Amount.3))), by=BuyerID]

# Or as a single line
DT[,   list(  Fruit = list(c( Fruit.1,  Fruit.2,  Fruit.3)), 
            Ammount = list(c(Amount.1, Amount.2, Amount.3)), 
            Total, Count),  # other columns used
            by = BuyerID]
Run Code Online (Sandbox Code Playgroud)

  • @jacatra:虽然这个解决方案可以帮助您实现您想要的目标,但我可以建议长期数据框"DTlong"(在此答案中作为中间步骤创建)从长远来看会更容易使用吗? (4认同)

ags*_*udy 6

这是一个带有基础包的解决方案.这就像泰勒解决方案,但只有一个适用.

res <- apply(DT,1,function(x){
  data.frame(Fruit= paste(na.omit(x[2:4]),collapse=' '),
             Amount = paste(na.omit(x[5:7]),collapse =','),
             Total = sum(as.numeric(na.omit(x[5:7]))),
             Count = length(na.omit(x[2:4])))
})
do.call(rbind,res)
                    Fruit  Amount Total Count
1            Banana Apple    4, 3     7     2
2 Strawberry Apple Orange 1, 2, 4     7     3
3           Orange Banana 1, 1, 1     3     2
4              Strawberry       3     3     1
5             Kiwi Banana    1, 2     3     2
Run Code Online (Sandbox Code Playgroud)

我也会用grep改变索引号,就像这样

 Fruit  = gregexpr('Fruit[.][0-9]', colnames(dat)) > 0  
 Amount = gregexpr('Amount[.][0-9]', colnames(dat)) > 0 

 x[2:4] replace by x[which(Fruit)]....
Run Code Online (Sandbox Code Playgroud)

编辑添加一些基准测试.

library(microbenchmark)
library(data.table)
microbenchmark(ag(),mn(), am(), tr())
Unit: milliseconds
  expr       min        lq    median        uq       max
1 ag() 11.584522 12.268140 12.671484 13.317934 109.13419
2 am()  9.776206 10.515576 10.798504 11.437938 137.44867
3 mn()  6.470190  6.805646  6.974797  7.290722  48.68571
4 tr()  1.759771  1.929870  2.026960  2.142066   7.06032
Run Code Online (Sandbox Code Playgroud)

对于小型数据框架,Tyler Rinker是赢家!我怎么解释这个(只是一个猜测)

  1. 数据:表解决方案使用重塑,一般data.table对大数据更快.
  2. 由于每行的子集化,Ag研究解决方案较慢,而不像使用apply之前的Tyler解决方案.
  3. 由于使用了重塑和合并,解决方案很慢.


Tyl*_*ker 5

这是一个非常糟糕的主意,但在这里是基础data.frame.它的工作原理data.frame实际上是一个等长矢量列表.你可以强制data.frame在单元格中存储向量,但它需要一些hackery.我建议其他格式,包括Marius的建议或列表.

DT <- data.frame(
  BuyerID = c(879,765,123,11,773), 
  Fruit.1 = c('Banana','Strawberry','Orange','Strawberry','Kiwi'),
  Fruit.2 = c('Apple','Apple','Banana',NA,'Banana'),
  Fruit.3 = c( NA, 'Orange',NA,NA,NA),
  Amount.1 = c(4,1,1,3,1), Amount.2 = c(3,2,1,NA,2), Amount.3 = c(NA,4,1,NA,NA),
  stringsAsFactors = FALSE)

DT2 <- DT[, 1, drop=FALSE]
DT2$Fruit <- apply(DT[, 2:4], 1, function(x) unlist(na.omit(x)))
DT2$Amount <- apply(DT[, 5:7], 1, function(x) unlist(na.omit(x)))
DT2$Total <- sapply(DT2$Amount, sum)
DT2$Count <- sapply(DT2$Fruit, length)
Run Code Online (Sandbox Code Playgroud)

产量:

> DT2
  BuyerID                     Fruit  Amount Total Count
1     879             Banana, Apple    4, 3     7     2
2     765 Strawberry, Apple, Orange 1, 2, 4     7     3
3     123            Orange, Banana 1, 1, 1     3     2
4      11                Strawberry       3     3     1
5     773              Kiwi, Banana    1, 2     3     2
Run Code Online (Sandbox Code Playgroud)