【问题标题】:Extract numbers from variable with string and numeric data using R使用R从具有字符串和数字数据的变量中提取数字
【发布时间】:2021-07-21 04:57:13
【问题描述】:

我正在尝试从 R 中数据框中的变量中提取数字,其中包括数字和文本。这是变量的示例:

ID  X

1   1 sandwich 2 hamburger

2   1 sandwich 4 salad 5 soda 7 soup

3   0 chicken wings

4   n/a

5   n/a

我在文本中有几个数字,并且数字/文本的数量是不均匀的。我只能通过执行以下操作来提取数字:

x_numbers <- regmatches(df$X, gregexpr("[[:digit:]]+", df$X))

我现在有一个将这些作为角色对象的列表。问题是我需要这些值在同一个数据框中,所以它们连接到同一个 ID(即同一行)。我需要的是以下内容:

ID X   X2  X3  X4

1  1   2   na  na

2  1   4   5   7

3  0   na  na  na

4  na  na  na  na

5  na  na  na  na

关于如何最好地做到这一点,以使数据仍然与 ID 相关联并按列分隔,以便在未来的分析中使用,您有什么想法吗?我对这些数据有超过 3000 次观察。谢谢!

【问题讨论】:

    标签: r list dataframe dplyr tidyverse


    【解决方案1】:

    这是您的数据:

    df <- data.frame(ID = 1:5,
                     X = c("1 sandwich 2 hamburger",
                           "1 sandwich 4 salad 5 soda 7 soup",
                           "0 chicken wings", 
                           NA, 
                           NA))
    

    注意:如果您使用 R>4.0,则无需指定 stringsAsFactors = FALSE,因为它是新的默认值。

    脏但有效

    library(dplyr) # only with version > 1.0
    library(stringr)
    
    df %>% 
      summarise(ID = ID,
                as.data.frame(do.call(rbind, str_extract_all(X, "[[:digit:]+]") %>% 
                  lapply(`length<-`, max(lengths(.))))))
    
    #>   ID   V1   V2   V3   V4
    #> 1  1    1    2 <NA> <NA>
    #> 2  2    1    4    5    7
    #> 3  3    0 <NA> <NA> <NA>
    #> 4  4 <NA> <NA> <NA> <NA>
    #> 5  5 <NA> <NA> <NA> <NA>  
    

    额外的想法

    我想指出,如果需要,您仍然可以保留有关文本的信息。 [这只是为了帮助你提供一些新的想法]

    library(tidyr)
    library(dplyr)
    
    tmp <- df %>% 
      separate_rows(X, sep = "(?<=.)(?=[[:digit:]+])") %>% 
      separate(X, c("n", "txt"), sep = " ", extra = "merge")
    tmp
    #> # A tibble: 9 x 3
    #>      ID n     txt            
    #>   <int> <chr> <chr>          
    #> 1     1 1     "sandwich "    
    #> 2     1 2     "hamburger"    
    #> 3     2 1     "sandwich "    
    #> 4     2 4     "salad "       
    #> 5     2 5     "soda "        
    #> 6     2 7     "soup"         
    #> 7     3 0     "chicken wings"
    #> 8     4 <NA>   <NA>          
    #> 9     5 <NA>   <NA>
    

    鉴于此,您可以使用以下代码获得所需的结果:

    tmp %>%
      group_by(ID) %>% 
      mutate(x = row_number()) %>% 
      ungroup() %>% 
      select(-txt) %>% 
      pivot_wider(names_from = x, values_from = n, names_prefix = "X")
    #> # A tibble: 5 x 5
    #>      ID X1    X2    X3    X4   
    #>   <int> <chr> <chr> <chr> <chr>
    #> 1     1 1     2     <NA>  <NA> 
    #> 2     2 1     4     5     7    
    #> 3     3 0     <NA>  <NA>  <NA> 
    #> 4     4 <NA>  <NA>  <NA>  <NA> 
    #> 5     5 <NA>  <NA>  <NA>  <NA>
    

    【讨论】:

    • 这太棒了!非常感谢江户!完美的修复,我使用你额外的想法代码也将文本保留在我的数据框中。非常感谢!
    • 不客气。我很高兴 Extras 有帮助。但是,请记住,对于您的具体问题,@Onyambu 的答案可能是最好和最干净的解决方案,如果我是您,会使用他的代码。我在他的答案中留下了代码以获得您正在寻找的预期输出。相反,如果您需要像我在 Extra Thoughts 段落中所做的那样保留文本信息,那么我想我的解决方案就是您正在寻找的。​​span>
    【解决方案2】:

    在基础 R 中,您可以这样做:

    read.table(text=gsub("\\D+", " ", df[,2]), fill=TRUE)
    
      V1 V2 V3 V4
    1  1  2 NA NA
    2  1  4  5  7
    3  0 NA NA NA
    4 NA NA NA NA
    5 NA NA NA NA
    6 NA NA NA NA
    

    【讨论】:

    • 爱它。赞成它。要得到完整的答案,你会绑定ID吗? cbind(ID = df[,1], read.table(text = gsub("\\D+", " ", df[,2]), fill = TRUE))
    • 感谢 Onyambu!也喜欢这个基本的 R 版本。在我的端运行它时遇到了一些问题(fill=TRUE 未使用,并且出现错误,表明我的变量的维数不正确)。
    【解决方案3】:
    my_data %>%
      mutate(x = str_extract_all(X, "[[:digit:]]+", simplify = TRUE))
    
    
      ID                                X  x.1 x.2 x.3 x.4
    1  1           1 sandwich 2 hamburger    1   2        
    2  2 1 sandwich 4 salad 5 soda 7 soup    1   4   5   7
    3  3                  0 chicken wings    0            
    4  4                             <NA> <NA>            
    5  5                             <NA> <NA>            
    6 NA                             <NA> <NA> 
    

    源数据

    my_data <- data.frame(
      stringsAsFactors = FALSE,
                    ID = c(1L, 2L, 3L, 4L, 5L, NA),
                     X = c("1 sandwich 2 hamburger",
                           "1 sandwich 4 salad 5 soda 7 soup","0 chicken wings",NA,
                           NA,NA)
    )
    

    【讨论】:

    • 打印看起来正确,但str() 的结果将显示您刚刚创建了一个新列,它是一个矩阵。我认为 NA_Psych 需要一个带有分隔列的数据框。
    • 明白了,让我再看看。
    • 感谢 Jon Spring 对此的帮助和想法!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2022-01-17
    相关资源
    最近更新 更多