基于模板更改数据框列的数据类型与 R 中的匹配列

Changing Data types of dataframe columns based on template with matching columns in R

我有 2 个数据框。

我想根据第一个更改第二个数据框的数据类型。假设我有以下用作模板的数据框。

> template
id <- c(1,2,3,4)
a <- c(1,4,5,6)
b <- as.character(c(0,1,1,4))
c <- as.character(c(0,1,1,0))
d <- c(0,1,1,0)
template <- data.frame(id,a,b,c,d, stringsAsFactors = FALSE)

> str(template)
'data.frame':   4 obs. of  5 variables:
 $ id: num  1 2 3 4
 $ a : num  1 4 5 6
 $ b : chr  "0" "1" "1" "4"
 $ c : chr  "0" "1" "1" "0"
 $ d : num  0 1 1 0

我正在寻找以下东西。

**注意-如果在 df 中不可用,它应该添加包含所有 NA 的附加列。

> df
id <- c(6,7,12,14,1,3,4,4)
a <- c(0,1,13,1,3,4,5,6)
b <- c(1,4,12,3,4,5,6,7)
c <- c(0,0,13,3,4,45,6,7)
e <- c(0,0,13,3,4,45,6,7)
df <- data.frame(id,a,b,c,e)

> str(df)
'data.frame':   8 obs. of  5 variables:
 $ id: num  6 7 12 14 1 3 4 4
 $ a : num  0 1 13 1 3 4 5 6
 $ b : num  1 4 12 3 4 5 6 7
 $ c : num  0 0 13 3 4 45 6 7
 $ e : num  0 0 13 3 4 45 6 7

期望的输出-

> output
    id  a  b  c  d
    1  6  0  1  0 NA
    2  7  1  4  0 NA
    3 12 13 12 13 NA
    4 14  1  3  3 NA
    5  1  3  4  4 NA
    6  3  4  5 45 NA
    7  4  5  6  6 NA
    8  4  6  7  7 NA

> str(output)

'data.frame':   8 obs. of  5 variables:
 $ id: num  6 7 12 14 1 3 4 4
 $ a : num  0 1 13 1 3 4 5 6
 $ b : chr  "1" "4" "12" "3" ...
 $ c : chr  "0" "0" "13" "3" ...
 $ d : logi  NA NA NA NA NA NA ...

我的尝试-

template <- fread("template.csv"),header=TRUE,stringsAsFactors = FALSE)
n <- names(template)
template[,(n) :=  lapply(.SD,function(x) gsub("[^A-Za-z0-90 _/.-]","", as.character(x)))]
n <- names(df)
df[,(n) :=  lapply(.SD,function(x) gsub("[^A-Za-z0-90 _/.-]","", as.character(x)))]
output <- rbindlist(list(template,df),use.names = TRUE,fill = TRUE,idcol="template")

在此之后,我编写了输出数据帧,然后使用 write.csv 重新读取以获取数据类型。但是,我弄乱了数据类型。请提出任何适当的处理方法。

这里有一些代码可以满足您的需求。

require(tidyverse)

new_types <-
    map_df(template, class) %>% 
    t %>%
    as.data.frame(stringsAsFactors = F) %>%
    rownames_to_column %>%
    setNames(c('col', 'type'))

new_data <- df %>%
    gather(col, value) %>%
    right_join(new_types, by='col') %>%
    group_by(col) %>%
    mutate(rownum = row_number()) %>%
    ungroup %>%
    complete(col, rownum=1:max(rownum)) %>%
    group_by(col) %>%
    summarize(val = list(value), type=first(type)) %>%
    mutate(new_val = map2(val, type, ~as(.x, .y, strict = T))) %>%
    select(col, new_val) %>%
    spread(col, new_val) %>%
    unnest

这里的主要思想是使用 purrr 包中的 map2() 来应用基础 R 中的 as() 函数。此函数接受一个对象(例如向量或列来自数据框)和描述新类型的字符串,以及 returns 强制对象。这是您需要的核心能力。

我的new_types数据框只列出了模板的列名和数据框中它们类型的(字符串)名称。

除了 map2() 行,其他所有内容都是混乱的数据争论,可能会得到改进。

一些主要特点:

  • right_join 这里很重要,只保留你想要的列。
  • 仅当目标 df 具有不在 template 中的列时,才需要从 mutate(rownum = row_number())complete(col, rownum=1:max(rownum)) 的行——它们确保结果NA 的数量与其他列相同。

我愿意

res = data.frame(
  lapply(setNames(,names(template)), function(x) 
    if (x %in% names(df)) as(df[[x]], class(template[[x]])) 
    else template[[x]][NA_integer_]
  ), stringsAsFactors = FALSE)

或使用 magrittr

library(magrittr)

setNames(, names(template)) %>% 
  lapply(. %>% {
    if (. %in% names(df)) as(df[[.]], class(template[[.]])) 
    else template[[.]][NA_integer_]
  }) %>% data.frame(stringsAsFactors = FALSE)

正在验证...

'data.frame':   8 obs. of  5 variables:
 $ id: num  6 7 12 14 1 3 4 4
 $ a : num  0 1 13 1 3 4 5 6
 $ b : chr  "1" "4" "12" "3" ...
 $ c : chr  "0" "0" "13" "3" ...
 $ d : num  NA NA NA NA NA NA NA NA

如果您打算做很多这样的事情,我建议您查看 vetr 包。它对数据框及其列的模板有很好的方法。