R在for循环中分离和合并--有没有更快/更容易/更好的方法?

vuv7lop3  于 2023-03-20  发布在  其他
关注(0)|答案(3)|浏览(163)

我收到的数据中包含需要拆分/分离的字段,如以下简化虚构(仅列出2个此类字段,但可能有2个、10个或无字段,并非所有字段都必须分离):

df <- data.frame(    MgrID =  c("111A", "111B", "111C"),
                     Empl = c("ATL\\Sam", "ATL\\Sarah", "MNL\\Ethel"),
                     Desk = c("ATL\\D123", "HOM\\H1", "MNL\\M456")
                )

为了拆分它们,我有一个列表,其中包含需要拆分的字段和拆分后应该使用的新名称(见下面的示例):

# ID which fields need to be separated within df above and what the new fields should be
    L1 <- c(Empl), c("City", "Name"))
    L2 <- c(Desk), c("Site", "Location"))
    ToSep <- list(L1, L2)

上面创建的“ToSep”列表在完成时将如下所示:

ToSep
   [[1]]
   [1] "Empl" "City" "Name"

   [[2]]
   [1] "Desk"     "Site"     "Location"

并且本质上是需要拆分的内容和新列名应该是什么的对(例如,将“员工”列拆分成“城市”和“姓名”列)。
我可以使用下面的for循环来分离字段并将其重新组合到现有的 Dataframe 中:

df1 <- df # Creating temp df to use inside for loop
    for (i in 1:length(ToSep)) {
      # Separate selected fields for each i  
      df2 <- df %>% 
        separate(ToSep[[i]][1], ToSep[[i]][-1], "\\\\", remove = FALSE, fill="left")
     
      # Merge current df1 with new df2 (df holding the split fields) 
      df1 <- merge(
        df1,
        df2,
        by = colnames(df),
        all = TRUE
      )
      
    }
df <- df1

这看起来有点笨拙,我想一定有一种方法可以简化,使更容易理解/更快/更好/更聪明/等等...

e0bqpujr

e0bqpujr1#

像这样的东西,也许?

for (el in ToSep) {
  df <- tidyr::separate(df, el[1], into = el[-1])
}
df
#   MgrID City  Name Site Location
# 1  111A  ATL   Sam  ATL     D123
# 2  111B  ATL Sarah  HOM       H1
# 3  111C  MNL Ethel  MNL     M456

或者更简单地说:

Reduce(function(df, el) separate(df, el[1], el[-1]),
       ToSep, init = df)

我注意到你的代码保持了原来的列不分隔。如果你仍然想要这样,试试这个:

Reduce(function(df, el) separate(df, el[1], el[-1]),
       ToSep, init = df) %>%
  bind_cols(df[sapply(ToSep, `[[`, 1)])
#   MgrID City  Name Site Location       Empl      Desk
# 1  111A  ATL   Sam  ATL     D123   ATL\\Sam ATL\\D123
# 2  111B  ATL Sarah  HOM       H1 ATL\\Sarah   HOM\\H1
# 3  111C  MNL Ethel  MNL     M456 MNL\\Ethel MNL\\M456

数据

df <- data.frame(    MgrID =  c("111A", "111B", "111C"),
                     Empl = c("ATL\\Sam", "ATL\\Sarah", "MNL\\Ethel"),
                     Desk = c("ATL\\D123", "HOM\\H1", "MNL\\M456")
                )

ToSep <- list(
  c("Empl", "City", "Name"),
  c("Desk", "Site", "Location")
)
disho6za

disho6za2#

我们可以将purrr包中的reduceleft_join()结合使用。
1.对于reduce(),我们迭代地将相同的函数应用于要分离的列的列表。
1.此函数使用separate()将列分隔为新的列名

  1. left_join()将结果 Dataframe 与原始 Dataframe 结合。
    1.最后,使用.init参数初始化原始 Dataframe 的缩减
library(purrr)
library(dplyr)
library(tidyr)

reduce(
  ToSep,
  function(df, x) {
    df %>% 
      separate(x[1], into = x[-1], sep = "\\\\", remove = FALSE, fill = "left") %>% 
      left_join(df, by = names(df))
  },
  .init = df
)

MgrID       Empl City  Name      Desk Site Location
1  111A   ATL\\Sam  ATL   Sam ATL\\D123  ATL     D123
2  111B ATL\\Sarah  ATL Sarah   HOM\\H1  HOM       H1
3  111C MNL\\Ethel  MNL Ethel MNL\\M456  MNL     M456
e5nqia27

e5nqia273#

基本上使用strsplit

lapply(df[-1], \(x) do.call('rbind', strsplit(x, split='\\\\'))) |>
  do.call(what='cbind.data.frame') |> setNames(c("City", "Name", "Site", "Location")) |> cbind(df)
#   City  Name Site Location MgrID       Empl      Desk
# 1  ATL   Sam  ATL     D123  111A   ATL\\Sam ATL\\D123
# 2  ATL Sarah  HOM       H1  111B ATL\\Sarah   HOM\\H1
# 3  MNL Ethel  MNL     M456  111C MNL\\Ethel MNL\\M456

相关问题