簡體   English   中英

使用tidyverse整理多組寬列的表格

[英]Tidying table with multiple groups of wide columns, using tidyverse

我經常遇到這樣的情況:我的表包含多組寬列,如下所示:

  replicate groupA        VA1         VA2 groupB         VB1        VB2
1         1      a  0.3429166 -2.30336406      f  0.05363582  1.6454078
2         2      b -1.3183732 -0.13516849      g -0.42586417  0.1541541
3         3      c -0.7908358 -0.10746447      h  1.05134242  1.4297350
4         4      d -0.9963677 -1.82557058      i -1.14532536  1.0815733
5         5      e -1.3634609  0.04385812      j -0.65643595 -0.1452877

我想將這些列變成一個長表,如下所示:

   replicate group key       value
1          1     a  V1  0.34291665
2          2     b  V1 -1.31837322
3          3     c  V1 -0.79083580
4          4     d  V1 -0.99636772
5          5     e  V1 -1.36346088
6          1     a  V2 -2.30336406
7          2     b  V2 -0.13516849
8          3     c  V2 -0.10746447
9          4     d  V2 -1.82557058
10         5     e  V2  0.04385812
11         1     f  V1  0.05363582
12         2     g  V1 -0.42586417
13         3     h  V1  1.05134242
14         4     i  V1 -1.14532536
15         5     j  V1 -0.65643595
16         1     f  V2  1.64540784
17         2     g  V2  0.15415408
18         3     h  V2  1.42973499
19         4     i  V2  1.08157329
20         5     j  V2 -0.14528774

我可以通過分別選擇兩組列,進行整理然后重新綁定在一起來實現此目的(下面的代碼)。 但是,這種方法似乎並不特別優雅,並且如果存在多於兩組的列,則將變得很麻煩。 我想知道是否存在使用單個管道數據轉換鏈的更優雅的方法。

這里的基本問題是:我們如何自動化將表分為幾列,整理一下然后重新組合在一起的過程。

我當前的代碼:

library(dplyr)
library(tidyr)

# generate example code
df_wide <- data.frame(replicate = 1:5,
                      groupA = letters[1:5],
                      VA1 = rnorm(5),
                      VA2 = rnorm(5),
                      groupB = letters[6:10],
                      VB1 = rnorm(5),
                      VB2 = rnorm(5))

# tidy columns with A in the name
dfA <- select(df_wide, replicate, groupA, VA1, VA2) %>%
  gather(key, value, VA1, VA2) %>%
  mutate(key = case_when(key == "VA1" ~ "V1",
                         key == "VA2" ~ "V2")) %>%
  select(replicate, group = groupA, key, value)

# tidy columns with B in the name
dfB <- select(df_wide, replicate, groupB, VB1, VB2) %>%
  gather(key, value, VB1, VB2) %>%
  mutate(key = case_when(key == "VB1" ~ "V1",
                         key == "VB2" ~ "V2")) %>%
  select(replicate, group = groupB, key, value)

# combine
df_long <- rbind(dfA, dfB)

注意: 在這里這里都曾提出類似的問題,但我認為已被接受的答案表明這是一個微妙的問題。

1個

雖然這個問題問了tidyverse的解決方案,有一個方便的選擇meltdata.table ,也可以采取多種patternsmeasure參數。

library(data.table)
setnames(melt(melt(setDT(df1), measure = patterns('group', 'VA', 'VB')), 
        id.var = 1:3)[, -4, with = FALSE], 2:3, c('key', 'group'))[]

2.一個

tidyverse我們可以子集的數據集到一個list ,然后通過循環listmap_df將其轉換為“長”格式與gather得到一個data.frame

library(tidyverse)
list(df1[1:4], df1[c(1,5:7)]) %>%
      map_df(~gather(., key, value, 3:4) %>%
                   {names(.)[2] <- 'group';.}) %>%
      mutate(key = sub('(.).(.)', '\\1\\2', key))
#   replicate group key       value
#1          1     a  V1  0.34291660
#2          2     b  V1 -1.31837320
#3          3     c  V1 -0.79083580
#4          4     d  V1 -0.99636770
#5          5     e  V1 -1.36346090
#6          1     a  V2 -2.30336406
#7          2     b  V2 -0.13516849
#8          3     c  V2 -0.10746447
#9          4     d  V2 -1.82557058
#10         5     e  V2  0.04385812
#11         1     f  V1  0.05363582
#12         2     g  V1 -0.42586417
#13         3     h  V1  1.05134242
#14         4     i  V1 -1.14532536
#15         5     j  V1 -0.65643595
#16         1     f  V2  1.64540780
#17         2     g  V2  0.15415410
#18         3     h  V2  1.42973500
#19         4     i  V2  1.08157330
#20         5     j  V2 -0.14528770

2.b

如果我們需要根據“組”的出現進行split

split.default(df1[-1], cumsum(grepl('group', names(df1)[-1]))) %>% 
         map(~bind_cols(df1[1], .)) %>% 
         map_df(~gather(., key, value, 3:4) %>% 
               {names(.)[2] <- 'group';.}) %>%
         mutate(key = sub('(.).(.)', '\\1\\2', key))

2.c

包含的rename_at代替了tidyverse選項精神的names分配

df1[-1] %>% 
      split.default(cumsum(grepl('group', names(df1)[-1]))) %>% 
      map_df(~bind_cols(df1[1], .) %>% 
           gather(., key, value, 3:4) %>%
           rename_at(2, funs(substring(.,1, 5))))

注意:

1) 2.a2.b2.c使用了dydyverse函數

2)它不依賴於列名稱中的子字符串“ A”或“ B”

3)假設OP數據集中的模式將是“組”,后跟值列

1)該解決方案包括:

  • 收集生成所需的行數
  • 結合了groupA和groupB列並將鍵列更改為請求的mutate
  • 選擇哪個選擇所需的列。

首先收集名稱以V開頭的列,然后從groupA和groupB創建一個新的group列,如果鍵中包含A,則選擇groupA,如果鍵中包含B,則選擇groupB。 (我們在這里使用mapply(switch,...)可以很容易地擴展到3+組的情況,但是我們可以使用ifelse,即ifelse(grepl(“ A”,key)as.character(groupA)as .character(groupB)),因為我們只有兩個組。)mutate還將鍵名從VA1減少到V1,依此類推,最后選擇所需的列。

DF %>% 
   gather(key, value, starts_with("V")) %>%
   mutate(group = mapply(switch, gsub("[^AB]", "", key), A = groupA, B = groupB),
          key = sub("[AB]", "", key)) %>%
   select(replicate, group, key, value)

給予:

   replicate group key       value
1          1     a  V1  0.34291660
2          2     b  V1 -1.31837320
3          3     c  V1 -0.79083580
4          4     d  V1 -0.99636770
5          5     e  V1 -1.36346090
6          1     a  V2 -2.30336406
7          2     b  V2 -0.13516849
8          3     c  V2 -0.10746447
9          4     d  V2 -1.82557058
10         5     e  V2  0.04385812
11         1     f  V1  0.05363582
12         2     g  V1 -0.42586417
13         3     h  V1  1.05134242
14         4     i  V1 -1.14532536
15         5     j  V1 -0.65643595
16         1     f  V2  1.64540780
17         2     g  V2  0.15415410
18         3     h  V2  1.42973500
19         4     i  V2  1.08157330
20         5     j  V2 -0.14528770

2)另一種方法是將列分成幾組,以便從名稱中刪除A和B后,組中的所有列都具有相同的名稱。 在每個這樣的組上執行不列出,以將列表簡化為純矢量列表,並將該列表轉換為data.frame。 最后收集V列並重新排列。 請注意,rownames_to_column來自tibble包。

DF %>%
   as.list %>%
   split(sub("[AB]", "", names(.))) %>%
   lapply(unlist) %>%
   as.data.frame %>%
   rownames_to_column %>%
   gather(key, value, starts_with("V")) %>%
   arrange(gsub("[^AB]", "", rowname), key) %>%
   select(replicate, group, key, value)

2a)如果行順序不重要,則可以省略rownames_to_column,range和select行,將其縮短為:

DF %>%
   as.list %>%
   split(sub("[AB]", "", names(.))) %>%
   lapply(unlist) %>%
   as.data.frame %>%
   gather(key, value, starts_with("V"))

解決方案(2)和(2a)可以很容易地轉換為僅基於鹼基的解決方案,方法是像第二個變形一樣,用適當的從底部進行的變形替換道集,即在(3)中產生d2的那個。

3)盡管這個問題要求一個整潔的解決方案,但是有一個相當方便的基本解決方案,它包含兩個重塑調用。 拆分產生的變化是: list(group = c("groupA", "groupB"), V1 = c("VA1", "VB1"), V2 = c("VA2", "VB2")) - -也就是說,它與每組列中的第i列匹配。

varying <- split(names(DF)[-1], gsub("[AB]", "", names(DF))[-1])
d <- reshape(DF, dir = "long", varying = varying, v.names = names(varying))
d <- subset(d, select = -c(time, id))

d2 <- reshape(d, dir = "long", varying = list(grep("V", names(d))), v.names = "value", 
  timevar = "key")
d2 <- subset(d2, select = c(replication, group, key, value))

d2

注意:可復制形式的輸入為:

DF <- structure(list(replicate = 1:5, groupA = structure(1:5, .Label = c("a", 
"b", "c", "d", "e"), class = "factor"), VA1 = c(0.3429166, -1.3183732, 
-0.7908358, -0.9963677, -1.3634609), VA2 = c(-2.30336406, -0.13516849, 
-0.10746447, -1.82557058, 0.04385812), groupB = structure(1:5, .Label = c("f", 
"g", "h", "i", "j"), class = "factor"), VB1 = c(0.05363582, -0.42586417, 
1.05134242, -1.14532536, -0.65643595), VB2 = c(1.6454078, 0.1541541, 
1.429735, 1.0815733, -0.1452877)), .Names = c("replicate", "groupA", 
"VA1", "VA2", "groupB", "VB1", "VB2"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM