在数据框中分隔一列,其中每个观察值可以有多个并发值

Separate a column in dataframe where each observation can have multiple concurrent values

我相信我的问题既是关于最佳实践的问题,也是关于整理杂乱数据的问题,所以这里开始吧。

以下是数据框 lang.df 的摘录,这是一个全校学生数据集。 Langauge.Home 列表示家长对问题的回答: "What languages do you speak at home?"

> lang.df
   Nationality             Language.Home
1           HK                  Mandarin
2       German   Mandarin/English/German
3        Saudi                    Arabic
4    Norwegian                 Norwegian
5           UK                   English
6           HK Mandarin/ Min Nan dialect
7   Australian                  Mandarin
8           HK                  Mandarin
9    Brazilian        Portuguese/English
10      Indian             Hindi/English

对我来说很明显,这是一种糟糕的获取信息的方式,也是一种糟糕的存储方式,但我的工作是使用我拥有的数据。

结果

我想探索某些家庭语言对成就的影响。我需要的是能够通过在家里说的一种语言(例如在家里说英语的学生)来划分子集。

为此,似乎我必须使用 dplyr 的 separate()Language@home 列分成三个 ("language.home1", "language.home2", "language.home3")。在我创建的新列中为每个唯一值(即语言)创建一个新列

进程

以下是我尝试有效地执行上述操作

library(dplyr)
library(tidyr)

#separate Langauge.Home into three new columns
lang.df <- lang.df %>% separate(Language.Home,
        c("language.home1", "language.home2", "language.home3"),
        sep = "/",
        remove = FALSE)

#find distinct languages & remove NAs
langs <- unique(c(lang.df$language.home1,
    lang.df$language.home2,
    lang.df$language.home3))
langs <- langs[!is.na(langs)]

#create boolean column for each unique language in new columns
for (i in langs) {
lang.df[,paste(i)] <- grepl(i, lang.df$Language.Home) 
}

问题

  1. 这种情况叫什么?我试图查看 tidyr 文档和此处的 SO,但找不到任何相关信息。
  2. 有没有比我所做的更优雅的转换编码方式?
  3. 什么是最佳实践
    • 获取此数据(以更改未来的数据输入过程)
    • 从统计角度处理这种情况

在此先感谢您的帮助。我断断续续地使用 R 大约一年了,这是我的第一个 SO post。尽可能多地给我反馈!

数据

lang.df <- structure(list(Nationality = structure(c(4L, 3L, 7L, 6L, 8L, 
4L, 1L, 4L, 2L, 5L), .Label = c("Australian", "Brazilian", "German", 
"HK", "Indian", "Norwegian", "Saudi", "UK"), class = "factor"), 
`Language.Home` = structure(c(4L, 6L, 1L, 7L, 2L, 5L, 4L, 
4L, 8L, 3L), .Label = c("Arabic", "English", "Hindi/English", 
"Mandarin", "Mandarin/ Min Nan dialect", "Mandarin/English/German", 
"Norwegian", "Portuguese/English"), class = "factor")), row.names = c(NA, 
10L), .Names = c("Nationality", "Language.Home"), class = "data.frame")

我们可以使用 splitstackshape 中的 cSplit 使用分隔符 / 拆分 'Language.Home' 并将其转换为 long 格式。

library(splitstackshape)
library(data.table)
dt <- cSplit(lang.df, "Language.Home", "/", "long")

然后,使用dcast将'long'转换为'wide'

dcast(dt, Nationality~Language.Home, fun.aggregate = function(x) length(x)>0)

注意:有重复的 'Nationality' 行,因此上面将把共同的元素组合在一起。把它组合在一起可能会更好。

如果我们需要有基于每一行的逻辑列(不考虑相似'Nationality')

 dcast(cSplit(setDT(lang.df, keep.rownames=TRUE), "Language.Home",
   "/", "long"), rn +Nationality ~Language.Home, function(x) length(x) >0)

或者另一个选项是 qdapTools 中的 mtabulate 在将 'Language.Home' 拆分为 / 之后。

 library(qdapTools)
 cbind(lang.df, !!(mtabulate(setNames(strsplit(as.character(lang.df$Language.Home), 
                 "/"), lang.df$Nationality))))
#   Nationality             Language.Home  Min Nan dialect Arabic English German Hindi Mandarin Norwegian Portuguese
#1           HK                  Mandarin            FALSE  FALSE   FALSE  FALSE FALSE     TRUE     FALSE      FALSE
#2       German   Mandarin/English/German            FALSE  FALSE    TRUE   TRUE FALSE     TRUE     FALSE      FALSE
#3        Saudi                    Arabic            FALSE   TRUE   FALSE  FALSE FALSE    FALSE     FALSE      FALSE
#4    Norwegian                 Norwegian            FALSE  FALSE   FALSE  FALSE FALSE    FALSE      TRUE      FALSE
#5           UK                   English            FALSE  FALSE    TRUE  FALSE FALSE    FALSE     FALSE      FALSE
#6           HK Mandarin/ Min Nan dialect             TRUE  FALSE   FALSE  FALSE FALSE     TRUE     FALSE      FALSE
#7   Australian                  Mandarin            FALSE  FALSE   FALSE  FALSE FALSE     TRUE     FALSE      FALSE
#8           HK                  Mandarin            FALSE  FALSE   FALSE  FALSE FALSE     TRUE     FALSE      FALSE
#9    Brazilian        Portuguese/English            FALSE  FALSE    TRUE  FALSE FALSE    FALSE     FALSE       TRUE
#10      Indian             Hindi/English            FALSE  FALSE    TRUE  FALSE  TRUE    FALSE     FALSE      FALSE

获得长格式的一种简单方法是使用 tidyr::unnest():

library(dplyr)
library(tidyr)
library(stringr)

lang.df %>% 
  mutate(Language.Home = str_split(Language.Home, "/")) %>% 
  unnest()
#>    Nationality    Language.Home
#> 1           HK         Mandarin
#> 2       German         Mandarin
#> 3       German          English
#> 4       German           German
#> 5        Saudi           Arabic
#> 6    Norwegian        Norwegian
#> 7           UK          English
#> 8           HK         Mandarin
#> 9           HK  Min Nan dialect
#> 10  Australian         Mandarin
#> 11          HK         Mandarin
#> 12   Brazilian       Portuguese
#> 13   Brazilian          English
#> 14      Indian            Hindi
#> 15      Indian          English

这是一个基本方法,总共只有几行

lang.df <- structure(list(Nationality = structure(c(4L, 3L, 7L, 6L, 8L, 4L, 1L, 4L, 2L, 5L), .Label = c("Australian", "Brazilian", "German", "HK", "Indian", "Norwegian", "Saudi", "UK"), class = "factor"), `Language.Home` = structure(c(4L, 6L, 1L, 7L, 2L, 5L, 4L, 4L, 8L, 3L), .Label = c("Arabic", "English", "Hindi/English", "Mandarin", "Mandarin/ Min Nan dialect", "Mandarin/English/German", "Norwegian", "Portuguese/English"), class = "factor")), row.names = c(NA, 10L), .Names = c("Nationality", "Language.Home"), class = "data.frame")

第二部分:新数据框,每种语言分成单独的列并按顺序标记

dd <- read.table(text = gsub('/\s*', ';', lang.df$Language.Home),
                 sep = ';', na.strings = '', fill = TRUE, as.is = TRUE,
                 col.names = paste0('lang.home', 1:3))
#      lang.home1      lang.home2 lang.home3
#   1    Mandarin            <NA>       <NA>
#   2    Mandarin         English     German
#   3      Arabic            <NA>       <NA>
#   4   Norwegian            <NA>       <NA>
#   5     English            <NA>       <NA>
#   6    Mandarin Min Nan dialect       <NA>
#   7    Mandarin            <NA>       <NA>
#   8    Mandarin            <NA>       <NA>
#   9  Portuguese         English       <NA>
#  10       Hindi         English       <NA>

第三部分:每种独特语言的逻辑指标

lang <- na.omit(sort(unique(unlist(dd))))
idx <- `colnames<-`(t(apply(dd, 1, function(x) lang %in% x)), lang)

#       Arabic English German Hindi Mandarin Min Nan dialect Norwegian Portuguese
#  [1,]  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
#  [2,]  FALSE    TRUE   TRUE FALSE     TRUE           FALSE     FALSE      FALSE
#  [3,]   TRUE   FALSE  FALSE FALSE    FALSE           FALSE     FALSE      FALSE
#  [4,]  FALSE   FALSE  FALSE FALSE    FALSE           FALSE      TRUE      FALSE
#  [5,]  FALSE    TRUE  FALSE FALSE    FALSE           FALSE     FALSE      FALSE
#  [6,]  FALSE   FALSE  FALSE FALSE     TRUE            TRUE     FALSE      FALSE
#  [7,]  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
#  [8,]  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
#  [9,]  FALSE    TRUE  FALSE FALSE    FALSE           FALSE     FALSE       TRUE
# [10,]  FALSE    TRUE  FALSE  TRUE    FALSE           FALSE     FALSE      FALSE

合并三个部分:

cbind(lang.df, dd, idx)

#    Nationality             Language.Home lang.home1      lang.home2 lang.home3 Arabic English German Hindi Mandarin Min Nan dialect Norwegian Portuguese
# 1           HK                  Mandarin   Mandarin            <NA>       <NA>  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
# 2       German   Mandarin/English/German   Mandarin         English     German  FALSE    TRUE   TRUE FALSE     TRUE           FALSE     FALSE      FALSE
# 3        Saudi                    Arabic     Arabic            <NA>       <NA>   TRUE   FALSE  FALSE FALSE    FALSE           FALSE     FALSE      FALSE
# 4    Norwegian                 Norwegian  Norwegian            <NA>       <NA>  FALSE   FALSE  FALSE FALSE    FALSE           FALSE      TRUE      FALSE
# 5           UK                   English    English            <NA>       <NA>  FALSE    TRUE  FALSE FALSE    FALSE           FALSE     FALSE      FALSE
# 6           HK Mandarin/ Min Nan dialect   Mandarin Min Nan dialect       <NA>  FALSE   FALSE  FALSE FALSE     TRUE            TRUE     FALSE      FALSE
# 7   Australian                  Mandarin   Mandarin            <NA>       <NA>  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
# 8           HK                  Mandarin   Mandarin            <NA>       <NA>  FALSE   FALSE  FALSE FALSE     TRUE           FALSE     FALSE      FALSE
# 9    Brazilian        Portuguese/English Portuguese         English       <NA>  FALSE    TRUE  FALSE FALSE    FALSE           FALSE     FALSE       TRUE
# 10      Indian             Hindi/English      Hindi         English       <NA>  FALSE    TRUE  FALSE  TRUE    FALSE           FALSE     FALSE      FALSE