【问题标题】:dplyr bind_rows does not preserve variable labeldplyr bind_rows 不保留变量标签
【发布时间】:2016-01-20 01:45:57
【问题描述】:

haven::read_dta 支持使用label 属性将变量标签从 Stata 导入 R。 Rstudio 还支持在视图窗格中显示这些标签。

但是,当使用dplyr::bind_rows(或rbind_all)绑定两个数据框时,不会保留标签。这是一个错误吗?

library(dplyr)
id <- 1:5
attr(id, "label") <- "unit id"

df1 <- tbl_df(data.frame(id)) # label is fine
df1$id
# [1] 1 2 3 4 5
# attr(,"label")
# [1] "unit id"

df2 <- tbl_df(data.frame(id)) # label is fine
df2$id
# [1] 1 2 3 4 5
# attr(,"label")
# [1] "unit id"

df_bound <- bind_rows(df1, df2) # label is gone
df_bound$id
# [1] 1 2 3 4 5 1 2 3 4 5

【问题讨论】:

  • 我编辑了因为不清楚你在说什么。
  • 有趣的问题。 This dplyr blog post 说“所有函数现在都应该将列属性从输入复制到输出......”
  • 以防万一,我尝试使用dplyr 的开发版本,即0.4.3.9000,但两者都不起作用。

标签: r dplyr r-haven


【解决方案1】:

一种解决方法是使用rbind 而不是bind_rows。然后,您必须确保列名相同。

使用setdiff(names(df1), names(df2)) 获取在df1 中但不在df2 中的列名,反之亦然setdiff(names(df2), names(df1))

【讨论】:

    【解决方案2】:

    Daniel Lüdeckesjlabelled 包是处理标记数据时此类问题的一个很好的解决方案。我使用copy_labels 函数解决了类似的问题:

    library(dplyr)  
    library(sjlabelled) 
    id <- 1:5  
    attr(id, "label") <- "unit id"  
    df1 <- tbl_df(data.frame(id))  
    str(df1)   
    # tibble [5 × 1] (S3: tbl_df/tbl/data.frame)  
    # $ id: int [1:5] 1 2 3 4 5  
    # ..- attr(*, "label")= chr "unit id"  
    df2 <- tbl_df(data.frame(id)) # label is fine  
    df_bound <- bind_rows(df1, df2) # label is gone  
    str(df_bound)  
    # tibble [10 × 1] (S3: tbl_df/tbl/data.frame)  
    #  $ id: int [1:10] 1 2 3 4 5 1 2 3 4 5   
    
    df_bound <- copy_labels(df_bound, df1)  
    df_bound_labelled <- df_bound %>% mutate_at(vars(id), as_labelled)
    str(df_bound_labelled)  
    # tibble [10 × 1] (S3: tbl_df/tbl/data.frame)  
    # $ id: int [1:10] 1 2 3 4 5 1 2 3 4 5  
    #  ..- attr(*, "label")= chr "unit id"  
    

    【讨论】:

      【解决方案3】:

      本着与@TommyFlyn 给出的答案相同的精神,还有由 Joseph Larmarange 维护的 labelled 包,特别是由 Daniel Ludecke (sjlabelled) 和 Hadley Wickham 撰写。

      假设df.ls,一个使用haven::read_dta 导入的数据框列表,它们有很多共同的变量,但不是全部。在这种情况下,使用dplyr::bind_rows 很方便,它不需要在所有数据帧中具有相同的变量(列)。正如 OP 所提到的,问题在于它“删除”了标签。我要补充一点,这仅适用于列表的数据框共有的变量。当某些变量在某些数据框中但不在其他数据框中时,它们会保留其标签。

      我们可以使用

      提取“丢失”标签
      common_col_names <- ## get the names of the columns common to all the dfs
          Reduce(intersect, lapply(df.ls,names))
      library(labelled)
      labs.ls <- ## a list of lists
          lapply(
              df.ls,
              function(x) {
                  labelled::var_label(
                                x[, common_col_names],
                                unlist = FALSE
                            )
              }
          )
      

      假设给定变量在df.ls 的所有数据帧中保持相同的标签。 common_col_names 的使用以及标签是常量的假设意味着labs.ls 的所有元素(列表)都是相同的。

      设置unlist = FALSE(默认值)允许在列表中包含不同的标签(而不是字符向量),这反过来又允许以下内容(来自labelled 文档):``对于数据帧,如果value 是一个命名列表,只有名称与数据框的列匹配的元素才会被考虑在内。如果 value 是字符向量,则标签应与 data.frame 的列的顺序相同。'' 如果列表的所有数据帧中的列/变量不相同,这将非常方便。

      请注意,检查 labs.ls 有助于检查标签在数据帧中是否实际上保持相同。

      然后您只需绑定列表中的不同数据框并分配提取的标签:

      df <- dplyr::bind_rows(df.ls)
      labelled::var_label(df) <- labs.ls[[1]]
      

      这里我们使用labs.ls[[1]],但由于我们只考虑所有数据帧共有的变量,并且我们假设这些变量的标签是恒定的,请注意,可以使用 2、3、...、length(df.ls) 而不是 1 .

      【讨论】:

        【解决方案4】:

        sjmisc::add_rowsdplyr::bind_rows 语法相似,并保留变量和值标签属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-02
          • 1970-01-01
          • 2019-01-15
          • 2019-08-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多