多列处理和动态命名新列

时间:2017-06-05 15:18:48

标签: r naming data-cleaning large-data

变量被错误地输入到多个列中,例如:“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”等)。

谢谢!

3 个答案:

答案 0 :(得分:0)

我们可以尝试使用splitstackshape

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

答案 1 :(得分:0)

我不确定你的例子是对的。例如,在第三行中,您获得了age_1和age_3的值,然后是该行的所需输出NA。

如果我已经理解了您尝试做的事情,那么将列转换为行,修复它们然后再转置回来会更容易。尝试使用&#39; tidyverse&#39;作为起点。 dplyr和tidyr。

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()行才能使其运行,因为您在同一行中有多个值。如果你只有一个值(我认为你应该这样做?)那么你可以删除这两行。没有它们会更好,因为如果你的数据错误,它就不会运行。

编辑以下评论。这将使任何重复的条目NA。

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 

答案 2 :(得分:0)

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

首先定义get_only,当给定列表时,将其转换为data.frame并将get_only应用于每一行。当给定一个向量时,它返回单个非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