在R语言中使用tidyr包的'separate'函数来分割数据框中的多列数据

4
大家早上好,我已经读了关于用R分割列的几篇文章,但是我找不到如何解决我的问题。
我想使用tidyr R包中的“separate”函数,根据分隔符将数据框的列分成两列。 我有这个数据框:
dat1 AIN5997 AIN7452 AIN8674 AIN9655 001 01/02 02/02 02/02 01/02 002 01/02 01/01 02/02 02/02 003 01/02 01/02 01/01 02/02 004 01/02 01/01 02/02 01/02 005 01/01 01/01 02/02 02/02 006 01/02 01/02 01/01 02/02
如果可能的话,我想将每一列都按“/”分成两部分,并保留列名(例如:AIN5997将变为AIN5997.1和AIN5997.2)
我认为可以使用“separate”,但是当我尝试使用“apply”时,无法将该过程扩展到框架的每一列(可能是因为“separate”逐个单独处理数据框的每一列)。 这实际上应该很容易,但我的R技能非常差!
有很多线程解释如何将一列分成两列,例如:Split a column of a data frame to multiple columns
但我找不到如何将该过程同时扩展到多个列。
非常感谢您的帮助,
祝一切顺利 :)

你想要一个包含“分离”或其他解决方案的解决方案吗? - amarchin
任何建议都将非常有用,非常感谢Amarchin :-) - Chrys
2个回答

3

诀窍在于按正确的顺序创建新名称,因此请确保您想要分隔的列事先排序。

NA 值的问题在于处理过程无法将它们拆分。因此,诀窍是用可以拆分的内容替换它们。检查一下:

library(dplyr)
library(tidyr)

# example dataset
dt = data.frame(id = 1:2,
                AIN5997  = c("01/02", "01/02"),
                AIN7452  = c("02/02", NA),
                AIN8674 = c("02/02","02/02"), stringsAsFactors = F)

# specify columns you want to separate (specify column positions)
input_names = names(dt)[2:4]

# create new names (you want each name twice)
new_names = expand.grid(input_names, 1:2) %>% 
  unite(v, Var1, Var2, sep=".") %>% 
  pull(v) %>% 
  sort()

dt %>%
  unite_("v", input_names) %>%                  # unite columns of interest
  mutate(v = gsub("NA", "NA/NA", v)) %>%        # replace NAs with something that can be separated
  separate(v, new_names, convert = F)           # separate elements and give new names

#   id AIN5997.1 AIN5997.2 AIN7452.1 AIN7452.2 AIN8674.1 AIN8674.2
# 1  1        01        02        02        02        02        02
# 2  2        01        02        NA        NA        02        02

我还提供了一个更好的解决方案。它会自动处理NA值,您不必担心列名及其顺序。

library(dplyr)
library(tidyr)
library(purrr)

# example dataset
dt = data.frame(id = 1:2,
                AIN5997  = c("01/02", "01/02"),
                AIN7452  = c("02/02", NA),
                AIN8674 = c("02/02","02/02"), stringsAsFactors = F)

# separate a given column of your initial dataset
f = function(x) { dt %>% select_("id", x) %>% separate_(x, paste0(x, c(".1",".2"))) }


names(dt)[2:4] %>%             # get names of columns you want to separate
  map(f) %>%                   # apply the function above to each name (will create a list of dataframes)
  reduce(left_join, by="id")   # join dataframes iteratively

#   id AIN5997.1 AIN5997.2 AIN7452.1 AIN7452.2 AIN8674.1 AIN8674.2
# 1  1        01        02        02        02        02        02
# 2  2        01        02      <NA>      <NA>        02        02

非常感谢你,安东尼奥。这个程序完美地运行了,除了一个小问题:我的基因型有时会出现缺失值,编码为<NA>(抱歉,我之前应该提到的),似乎命令无法将它们分成两个新的<NA>值。另外,我很好奇:这个命令是如何知道要根据“/”来拆分主要值的呢?再次感谢你! - Chrys
1
谢谢Antonio,我将用“00/00”替换我的NA值,并查看您的链接!最好的问候,Chrys - Chrys
是的,那会起作用。我正在遵循相同的方法(即使用“NA/NA”替换)。我做了一个小而有用的改变。我在separate命令中添加了convert = .,这将使新列是数值型的,当你将其设置为T时,它就会理解你有数值型的值。在这种情况下,你会失去数字前面的零。 - AntoniosK
1
太好了 :-) 这正是我所需要的,非常感谢您的帮助!祝您有一个愉快的一天,Chrys - Chrys
很好。为了与我的输出完全匹配,我将保留答案中的 convert = F。如果您喜欢数字值并且不介意失去零(即使用 2 而不是 02),请随意使用 convert = T - AntoniosK
显示剩余4条评论

1

您还可以使用tstrsplit()函数。

# example dataset
df <- data.frame(AIN5997  = c("01/02", "01/02"),
                 AIN7452  = c("02/02","01/01"),
                 AIN8674 = c("02/02","02/02"), stringsAsFactors = F)
df
df2 <- as.data.frame(unlist(lapply(df, data.table::tstrsplit, "/"),
                            recursive = FALSE))
df2
colnames(df2) # change colnames
colnames(df2) <- paste(substr(colnames(df2), 1, nchar(colnames(df2))-1),
                       substr(colnames(df2), nchar(colnames(df2)), nchar(colnames(df2))),
                       sep = ".")
df2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接