【问题标题】:How to separate one column to multiple column (complex column)如何将一列分离为多列(复杂列)
【发布时间】:2017-12-12 14:05:25
【问题描述】:

我正在尝试根据主题和等级将“等级”列分成多个列

    grade<-read.csv("https://raw.githubusercontent.com/tuyenhavan/Statistics/Dataset/High_school_Grade.csv",sep=";")

# Rename the column names

    names(grade)<-c("Student_ID","Name","Venue","Grade")

    head(grade)

    # Separate `Grade` into `subject` variables and coresponding `Grade`columns
    library(tidyverse)


    df<- grade %>% separate(Grade,paste("V",1:7,sep="_"),sep=":")

    head(df)

    # It still is not separating `subject ` and `grade` independently

    # Here is what I want it to look like

    new_df<-df[c(1:5),c(1:4)]

    new_df<-data.frame(new_df, V2=c(1:5)) # the same for V2,4,5,6,,7 to separate subject and grade

    new_df 

我正在尝试使用 dplyr 和 stringr,但无法产生预期的结果

【问题讨论】:

    标签: r string data-manipulation


    【解决方案1】:

    这是使用tidyverse 包的一次尝试。在将所有内容转换为字符(即grade[] &lt;- lapply(grade, as.character))后,我们创建了一个自定义函数,该函数为每个StudentID 返回排序后的subject:grade。然后我们用unnest把它变长,用separate把它分成两列; SubjectGrade。最后我们spread 为每个主题获取一列。

    library(tidyverse)
    
    #This function could definetely be more elegant or even avoided
    #  but this is as far as my regex knowledge allows me to go
    
    mysplit <- function(x){
      y <- strsplit(x, ':\\s+|\\s+')[[1]]
      z <- paste0(y[c(T, F)], ': ', y[c(F, T)])
      return(z[order(sub(':.*', '', z))])
    }
    
    grade %>% 
      mutate(Grade = lapply(Grade, mysplit)) %>% 
      unnest() %>% 
      separate(Grade, into = c('Subject', 'Grade'), sep = ': ') %>% 
      spread(Subject, Grade)
    

    这样拆分:

    ...     Biology Chemitry English Geography History Literature Math Physics
    ...   1    6.00     6.00    <NA>      <NA>    <NA>       7.50 4.25    6.80
    ...   2    5.80     6.00    <NA>      <NA>    <NA>       6.00 5.75    <NA>
    ...   3    <NA>     <NA>    <NA>      8.00    4.50       7.75 2.25    <NA>
    ...   4    <NA>     <NA>    <NA>      7.25    7.50       7.75 3.25    <NA>
    ...   5    <NA>     <NA>    <NA>      7.75    4.50       8.25 1.75    <NA>
    ...   6    <NA>     6.60    6.78      <NA>    <NA>       7.00 8.75    8.40
    .
    .
    

    为了更好地理解这个函数,你应该把它分解。 说 x 如下:

    x
    #[1] "Math:   4.25   Literature:   7.50   Physics:   6.80   Chemitry:   6.00   Biology:   6.00"
    

    将其每space: space拆分,得到以下向量

    y <- strsplit(x, ':\\s+|\\s+')[[1]]
    y
     #[1] "Math"       "4.25"       "Literature" "7.50"       "Physics"    "6.80"       "Chemitry"   "6.00"       "Biology"    "6.00"
    

    将所有第一个元素(即科目,y[c(TRUE, FALSE)])粘贴在一起,然后将所有第二个元素(即成绩y[c(FALSE, TRUE)])用:分隔符粘贴在一起

    z <- paste0(y[c(T, F)], ': ', y[c(F, T)])
    z
    #[1] "Math: 4.25"       "Literature: 7.50" "Physics: 6.80"    "Chemitry: 6.00"   "Biology: 6.00"   
    

    最后它输出一个排序好的(基于单词sub(':.*', '', z))向量

    z[order(sub(':.*', '', z))]
    #[1] "Biology: 6.00"    "Chemitry: 6.00"   "Literature: 7.50" "Math: 4.25"       "Physics: 6.80"
    

    正如@rosscova 指出的那样,不需要对字符串进行排序,这大大简化了这一点(毕竟不需要函数),即

    grade %>% 
      mutate(Grade = strsplit(Grade, '[0-9]\\s+')) %>% 
      unnest() %>% 
      separate(Grade, into = c('Subject', 'Grade'), sep = ': ') %>% 
      spread(Subject, Grade)
    

    【讨论】:

    • 谢谢@Sotos。这对我来说很难理解。您能否在不使用 function 的情况下写下这个
    • 什么意思?分割等级变量需要该函数
    • 非常感谢!真的很有帮助
    • 很好的解决方案。我认为您的mysplit 函数可能只是一行:strsplit(x, '[0-9]\\s+')[[1]]。无需费心拆分“:”字符串,也无需排序。
    • @rosscova 你是对的。我不自觉地认为他们需要分类,我不可避免地想多了。感谢您了解这一点。
    【解决方案2】:

    在下面的解决方案中,我使用了 tidyverserebus 包中的函数。 rebus 包使用人类可读的代码逐段构建正则表达式。

     library(tidyverse)
     library(rebus)
     grade<-read.csv("https://raw.githubusercontent.com/tuyenhavan/Statistics/Dataset/High_school_Grade.csv",
                     sep = ";", stringsAsFactors = FALSE)
    
     grade_new <- grade %>%
       mutate(DIEM_THI2 = str_replace_all(DIEM_THI, pattern = ":" %R% one_or_more(SPC), "-")) %>%
       separate_rows(DIEM_THI2, sep = one_or_more(SPC)) %>%
       separate(DIEM_THI2, c("SUBJECT", "GRADE"), sep = "-") %>%
       spread(SUBJECT,GRADE)
    

    生成的数据框如下所示:

    head(grade_new[,5:12])
    #   Biology Chemitry English Geography History Literature Math Physics
    # 1    6.00     6.00    <NA>      <NA>    <NA>       7.50 4.25    6.80
    # 2    5.80     6.00    <NA>      <NA>    <NA>       6.00 5.75    <NA>
    # 3    <NA>     <NA>    <NA>      8.00    4.50       7.75 2.25    <NA>
    # 4    <NA>     <NA>    <NA>      7.25    7.50       7.75 3.25    <NA>
    # 5    <NA>     <NA>    <NA>      7.75    4.50       8.25 1.75    <NA>
    # 6    <NA>     6.60    6.78      <NA>    <NA>       7.00 8.75    8.40
    

    代码可以这样理解:

    1. 所有冒号+空格子字符串都替换为连字符。即"Math: 4.25 Literature: 7.50" 变为"Math-4.25 Literature-7.50"。这是使用str_replace_all 函数完成的。让我们调用新变量DIEM_THI2
    2. separate_rows 函数将空格分隔的列 DIEM_THI2 拆分为单独的行,即 "Math-4.25""Literature-7.50" 跨越两个不同的行。
    3. DIEM_THI2 列分为两列,即SUBJECTGRADE,其中前者包含"Math""Literature" 等值,后者包含"4.25""7.50" 等值。
    4. 键值对或 SUBJECT-GRADE 对分布在多个列中。

    【讨论】:

    • 非常感谢@HNSKD。这个真的很容易理解
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    相关资源
    最近更新 更多