【问题标题】:Changing Data types of dataframe columns based on template with matching columns in R基于模板在R中使用匹配列更改数据框列的数据类型
【发布时间】:2018-07-08 12:24:13
【问题描述】:

我有 2 个数据框。

  • 模板 - 我将使用此数据框中的数据类型。
  • df - 我想根据模板更改此数据框的数据类型。

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

> 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中将模板的数据类型转换为完全相同。
  • 它应该具有与模板框架中相同的列。

**注意 - 如果 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 重新读取以获取数据类型。但是,我搞砸了数据类型。请提出任何适当的处理方法。

【问题讨论】:

    标签: r dataframe dplyr data.table fread


    【解决方案1】:

    我愿意

    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 包。它为数据框及其列的模板提供了一种很好的方法。

    【讨论】:

    • 惊人的解决方案!这有帮助。
    • 如果我想要 df 中的所有列以及模板但类类型与模板相同,并将 NA 分配给 df 中不可用的列。
    • @Rushabh 我认为您可以将上面的names(template) 替换为union(names(df), names(template))
    • 我尝试将其替换为union(names(df), names(template)),但没有成功
    • @Rushabh 也许发布一个新问题?我真的不记得这个问题的细节,可能不是处理它的最佳人选。
    【解决方案2】:

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

    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() 函数。该函数接受一个对象(例如,数据帧中的向量或列)和字符串描述一种新类型,并返回强制对象。这是您需要的核心能力。

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

    除了map2() 行之外,其他所有内容都是肮脏的数据争吵,可能会得到改进。

    一些关键特性:

    • right_join 这里对于只保留您想要的列至关重要。
    • mutate(rownum = row_number())complete(col, rownum=1:max(rownum)) 的行只有当目标df 包含不在template 中的列时才需要——它们确保NAs 的结果数与用于其他列。

    【讨论】:

      猜你喜欢
      • 2017-05-09
      • 2014-05-14
      • 1970-01-01
      • 2020-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多