多列处理和动态命名新列

Multiple columns processing and dynamically naming new columns

变量被错误地输入到多个列中,例如:"aaa_1"、"aaa_2" 和 "aaa_3",或 "ccc_1, "ccc_2" 和 "ccc_3")。需要创建单个新列(例如 "aaa",或 "ccc")。有些变量目前位于单个列中("hhh_1"),但可以添加更多列( hhh_2 等)。

这是我得到的:

aaa_1 <- c(43, 23, 65, NA, 45)  
aaa_2 <- c(NA, NA, NA, NA, NA)    
aaa_3 <- c(NA, NA, 92, NA, 82)  
ccc_1 <- c("fra", NA, "spa", NA, NA)   
ccc_2 <- c(NA, NA, NA, "wez", NA)  
ccc_3 <- c(NA, "ija", NA, "fda", NA)    
ccc_4 <- c(NA, NA, NA, NA, NA)
hhh_1 <- c(183, NA, 198, NA, 182)    
dataf1 <- data.frame(aaa_1,aaa_2,aaa_3,ccc_1,ccc_2, ccc_3,ccc_4,hhh_1)  

这就是我想要的:

aaa <- c(43, 23, NA, NA, NA)
ccc <- c("fra", "ija", "spa", NA, NA) 
hhh <- c(183, NA, 198, NA, 182)   
dataf2 <- data.frame(aaa,ccc,hhh)

需要通用解决方案,因为有 ~100 个变量(例如 "aaa"、"hhh"、"ccc"、"ttt"、"eee"、"hhh"等)。

谢谢!

我们可以试试 splitstackshape

library(splitstackshape)
nm1 <- sub("_\d+", "", names(dataf1))
tbl <- table(nm1) > 1
merged.stack(dataf1, var.stubs = names(tbl)[tbl], sep="_")

我不确定你举的例子是否正确。例如,在第三行中,您有 age_1 和 age_3 的值,然后在该行的所需输出 NA 中。

如果我已经理解您要执行的操作,那么如果您将列转置为行、修复它们然后再次转置回来,就会容易得多。尝试使用 dplyr 和 tidyr 的 'tidyverse' 作为起点。

library(tidyverse)
library(stringr)

age_1 <- c(43, 23, 65, NA, 45)
age_2 <- c(NA, NA, NA, NA, NA)
age_3 <- c(NA, NA, 92, NA, 82)
country_1 <- c("fra", NA, "spa", NA, NA)
country_2 <- c(NA, NA, NA, "wez", NA)
country_3 <- c(NA, "ija", NA, "fda", NA)
country_4 <- c(NA, NA, NA, NA, NA)
hight_1 <- c(183, NA, 198, NA, 182)
dataf1 <- data.frame(age_1,age_2,age_3,country_1,country_2, country_3,country_4,hight_1)

data <- dataf1 %>%
  mutate(row_num = row_number()) %>%   #create a row number to track values
  gather(key, value, -row_num) %>%    #flatten your data
  drop_na() %>%    #drop na rows
  mutate(key = str_replace(key, "_.", "")) %>%   #remove the '_x' part of names
  group_by(row_num) %>%  
  top_n(1) %>%
  spread(key, value)  #pivot back to columns

对于您的示例,您需要 group_by() 和 top_n() 行使其成为 运行,因为您在同一行中有多个值。如果您只有一个值(我认为您应该这样做?),那么您可以删除这两行。没有它们会更好,因为如果您的数据有误,它就不会 运行。

编辑下面的评论。这将使任何重复的条目不适用。

data <- dataf1 %>%
  mutate(row_num = row_number()) %>%   #create a row number to track values
  gather(key, value, -row_num) %>%    #flatten your data
  drop_na() %>%    #drop na rows
  mutate(key = str_replace(key, "_.", "")) %>%   #remove the '_x' part of names
  group_by(row_num, key) %>%
  mutate(count = n()) %>%  #count how many entries for each row/key combo
  mutate(value = ifelse(count > 1, NA, value)) %>%   #set NA for rows with duplicates
  drop_na() %>%
  spread(key, value) %>%  #pivot back to columns
  select(-count)  #drop the `count` variable 

这是基本解决方案,即没有包。

首先定义 get_only ,当给定一个列表时,它会将其转换为 data.frame 并将 get_only 应用于每一行。当给定一个向量时,它 returns 其中的单个非 NA 或 NA 如果不止一个。

定义root为不带后缀的列名。

将数据框转换为列列表,按 root 对它们进行分组,并将 get_only 应用到每个这样的组。

最后,将结果列表转换为数据框。

get_only <- function(x) UseMethod("get_only")
get_only.list <- function(x) apply(data.frame(x), 1, get_only)
get_only.default <- function(x) if (sum(!is.na(x)) == 1) na.omit(x) else NA

root <- sub("_.*", "", names(dataf1))
as.data.frame(lapply(split(as.list(dataf1), root), FUN = get_only))

给予:

  age country hight
1  43     fra   183
2  23     ija    NA
3  NA     spa   198
4  NA    <NA>    NA
5  NA    <NA>   182