【问题标题】:Reshape data within groups - groups in a single row [duplicate]重塑组内的数据-单行中的组[重复]
【发布时间】:2018-07-09 02:46:53
【问题描述】:

这是我的输入数据:

 DeviceID   ContentID   Use
 D1 C1  0.678491346
 D1 C2  0.302147374
 D2 C1  0.695790066
 D2 C2  0.645849165
 D3 C1  0.83503997
 D3 C2  0.3622916

预期输出:

DeviceID    ContentID_1 Use_1   ContentID_2 Use_2
D1  C1  0.678491346 C2  0.302147374
D2  C1  0.695790066 C2  0.645849165
D3  C1  0.83503997  C2  0.3622916

我尝试使用 reshape2 对其进行重塑,但无法以所需的格式获得它。

我试过了:

 df %>% 
   group_by(DeviceID) %>% 
   mutate(rn = paste0("Content",row_number())) %>% 
   spread(rn, Use)

dcast(df,
      DeviceID~ContentID,
      value.var ="Use")

任何帮助将不胜感激!

【问题讨论】:

  • 你是怎么尝试的?
  • dcast(df,Device ID~Content ID, value.var ="Use")。但我将内容 ID 作为列。
  • 但我不知道如何将内容 ID 连续获取
  • 请将此尝试添加到您的问题中。

标签: r dplyr reshape2


【解决方案1】:

从 v1.9.6 开始(CRAN 2015 年 9 月 19 日),data.table 可以同时转换多个值列:

library(data.table)
dcast(setDT(df), DeviceID ~ rowid(DeviceID), value.var = c("ContentID", "Use"))
    DeviceID  ContentID_1  ContentID_2     Use_1     Use_2
1:        D1           C1           C2 0.6784913 0.3021474
2:        D2           C1           C2 0.6957901 0.6458492
3:        D3           C1           C2 0.8350400 0.3622916

更改列顺序

结果包含预期的列,但顺序不同。 dcast() 创建按value.var 分组的新列。

OP 没有指出确切的列顺序是否重要。但是,可以通过按引用更改列顺序来准确再现预期结果,即,无需使用setcolorder()复制整个数据对象:

cols <- c("ContentID", "Use")
wide <- dcast(setDT(df), DeviceID ~ rowid(DeviceID), value.var = cols)
new_col_order <- CJ(seq_len(uniqueN(df$ContentID)), cols)[, paste(V2, V1, sep = "_")]
setcolorder(wide, new_col_order)
wide
   ContentID_1     Use_1 ContentID_2     Use_2 DeviceID
1:          C1 0.6784913          C2 0.3021474       D1
2:          C1 0.6957901          C2 0.6458492       D2
3:          C1 0.8350400          C2 0.3622916       D3

CJ() 是行 ID 与 value.vars 的交叉连接,用于按所需顺序创建列名。

我已提交feature request on GitHub 以选择性地更改dcast() 中的列顺序。

数据

library(data.table)
df <- fread(
  " DeviceID  ContentID  Use
 D1 C1  0.678491346
  D1 C2  0.302147374
  D2 C1  0.695790066
  D2 C2  0.645849165
  D3 C1  0.83503997
  D3 C2  0.3622916"
)

【讨论】:

    【解决方案2】:

    我们可以使用Reduceaggregate按照上面给出的格式重新排列它

    data.frame(Reduce(cbind,aggregate(.~Device,dat,I)))[c(1,2,4,3,5)]
      init V2          V4 V3          V5
    1   D1 C1 0.678491346 C2 0.302147374
    2   D2 C1 0.695790066 C2 0.645849165
    3   D3 C1  0.83503997 C2   0.3622916
    

    这是库 dplyr 的蛮力

     map2_dfc(s<-dat%>%spread(Id,Content),names(s),~rev(stack(s,.y)))
         ind values ind1   values1 ind2   values2
    1 Device     D1   C1 0.6784913   C2 0.3021474
    2 Device     D2   C1 0.6957901   C2 0.6458492
    3 Device     D3   C1 0.8350400   C2 0.3622916
    

    【讨论】:

    • 谢谢!有没有办法使用 dplyr/reshape2 做到这一点?
    • 我现在想不出任何东西。但如果有的话,会在这里发布。
    • 你能解释一下aggregate(.~Device,dat,I)吗?
    • aggregate 接收数据并根据提供的因子对其进行分组,然后对分组数据执行功能。我的功能是I (身份)功能。本质上只是划分数据但输出相同的数据
    • 例如试试aggregate(.~Species,iris,mean)
    【解决方案3】:

    发布我的解决方案:

    library(splitstackshape)
    library(tidyverse)
    
    df %>%
      group_by(DeviceId) %>%
      summarise_all(function(x) paste0(x, collapse = "_")) %>%
      cSplit(names(.)[-1], '_')
    

    在 cmets 中包含来自 @AntoniosK 的建议。

    【讨论】:

      【解决方案4】:
      library(data.table)
      DT <- setDT(df)
      

      使用您的 dcast 意图,您可以这样做

          Reduce(function(dtf1,dtf2) merge(dtf1,dtf2, by = "DeviceId"), 
                 lapply( unique(DT$ContentID),
                         function(x){dcast(DT[ContentID == x],DeviceId + ContentID  ~ ContentID ,value.var = "Use")} ))
      
         DeviceId ContentID.x        C1 ContentID.y        C2
      1:       D1          C1 0.6784913          C2 0.3021474
      2:       D2          C1 0.6957901          C2 0.6458492
      3:       D3          C1 0.8350400          C2 0.3622916
      

      【讨论】:

        【解决方案5】:
        df = read.table(text = "
        DeviceId  ContentID  Use
        D1 C1  0.678491346
        D1 C2  0.302147374
        D2 C1  0.695790066
        D2 C2  0.645849165
        D3 C1  0.83503997
        D3 C2  0.3622916
        ", header=T, stringsAsFactors=F)
        
        library(tidyverse)
        
        df %>%
          group_by(DeviceId) %>%
          summarise_all(function(x) paste0(x, collapse = "_")) %>%
          separate(ContentID, c("ID_1","ID_2"), sep="_") %>%
          separate(Use, c("Use_1","Use_2"), sep="_")
        
        # # A tibble: 3 x 5
        #   DeviceId ID_1  ID_2  Use_1       Use_2      
        # * <chr>    <chr> <chr> <chr>       <chr>      
        # 1 D1       C1    C2    0.678491346 0.302147374
        # 2 D2       C1    C2    0.695790066 0.645849165
        # 3 D3       C1    C2    0.83503997  0.3622916 
        

        【讨论】:

        • 如果我给出了两个以上的类别?我的实际数据有 n 个类别,有没有办法使 into 中的 into 参数动态化?
        • 我希望有一个 separate_all 函数 :-) 您可能必须创建自己的函数或使用不同的包。我去看看。
        • df %&gt;% group_by(DeviceId) %&gt;% summarise_all(function(x) paste0(x, collapse = "_")) %&gt;% cSplit(c('ContentID', 'Use'), '_') using cSplit' from the splitstackshape` 包 感谢@AntoniosK 为我指明了正确的方向!
        • 太棒了!或者 cSplit(names(.)[-1], '_') 如果您想自动排除第一列,而不输入所有感兴趣的列名...
        • 另外请记住,在summarise_all 中,您可以使用funs() 参数,例如summarise_all(funs(paste0(., collapse = "_")))。这会给你同样的结果。但是,funs() 可用于将多个函数应用于多个列,例如:summarise_all(funs(a = paste0(., collapse = "_"), b = paste0(., collapse = "||"))),这在将来的不同情况下可能会非常有用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-11
        • 2020-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-27
        • 2020-03-18
        相关资源
        最近更新 更多