如何从 csv/dataframe 列中的字符串解析数组数组

How to parse array of arrays from string in column of csv/dataframe

提问人:94621 提问时间:3/8/2023 更新时间:3/8/2023 访问量:191

问:

我有一个 csv 文件,其中一列包含一个 numpy 数组。读取 csv 文件时,生成的列将具有字符类型,因为它全部包装在字符串中。 我想将其解析为单独的数据帧来分析数据。

输入数据

作为 csv:

first_column,second_column
a,"[[1,2],[3,4]]"
b,"[[5,6],[7,8]]"
c,"[[9,10],[11,12]]"

作为数据帧:

df <- data.frame(first_column  = c("a","b","c"),
                 second_column = c("[[1,2],[3,4]]","[[5,6],[7,8]]","[[9,10],[11,12]]"))

我试过什么

由于我不知道有任何可以从字符串中提取数组的直接解析函数,所以我开始做字符串操作。

删除外部字符:[]

> df %>% mutate(second_column = str_replace_all(second_column, c("^\\[" = "","]$" = "")))
  first_column  second_column
1            a    [1,2],[3,4]
2            b    [5,6],[7,8]
3            c [9,10],[11,12]

但是,从现在开始,我不知道该如何进行。

预期输出

最终生成的数据帧应如下所示:

  col_1 col_2
1     1     2
2     3     4
3     5     6
4     7     8
5     9    10
6    11    12

请注意,实际数据帧中有更多的列和行

R 数组 数据帧 csv 嵌套列表

评论


答:

0赞 Matt 3/8/2023 #1

这是一个黑客解决方案,使用:tidyverse

df <- data.frame(first_column  = c("a","b","c"),
                 second_column = c("[[1,2],[3,4]]","[[5,6],[7,8]]","[[9,10],[11,12]]"))

library(tidyverse)

df %>% 
  mutate(second_column = str_replace_all(second_column, c("^\\[" = "","]$" = "")),
         second_column = gsub("\\[|\\]", "", second_column)) %>% 
  separate(second_column, into = c("col_1", "col_2", "col_3", "col_4"), sep = ",") %>% 
  pivot_longer(-first_column) %>% 
  mutate(name = case_when(name == "col_3" ~ "col_1",
                          name == "col_4" ~ "col_2", 
                          .default = name)) %>% 
  select(-first_column) %>% 
  pivot_wider(names_from = name, values_from = value, values_fn = list) %>% 
  unnest(cols = c(col_1, col_2))
  
#> # A tibble: 6 × 2
#>   col_1 col_2
#>   <chr> <chr>
#> 1 1     2    
#> 2 3     4    
#> 3 5     6    
#> 4 7     8    
#> 5 9     10   
#> 6 11    12
1赞 Andre Wildberg 3/8/2023 #2

一种基本 R 方法,用于处理给定列上的任意行数。

setNames(
  data.frame(Vectorize(\(x) as.numeric(x))(
    data.frame(do.call(rbind, 
      sapply(lapply(strsplit(df$second_column, "\\],\\["), 
          gsub, pattern="\\[|\\]", replacement=""), strsplit, ","))))), 
  c("col_1", "col_2"))
  col_1 col_2
1     1     2
2     3     4
3     5     6
4     7     8
5     9    10
6    11    12
3赞 G. Grothendieck 3/8/2023 #3

将出现的 ],[ 替换为换行符,将方括号替换为空格,并用于读取该值。read.table

df$second_column |>
  gsub("\\],\\[", "\n", x = _) |>
  chartr("[]", "  ", x = _) |>
  read.table(text = _, sep = ",")

给:

  V1 V2
1  1  2
2  3  4
3  5  6
4  7  8
5  9 10
6 11 12

评论

0赞 94621 3/8/2023
这看起来很整洁!这将导致宽表布局:当 CSV 有 10 行且嵌套表的大小为 10x10 时,生成的表将为 10 x 100 (10x10 = 100)
2赞 GKi 3/8/2023 #4

基本变体可以是:

#Replace [], with space
. <- gsub("[][,]", " ", df$second_column)
#. <- chartr("[],", "   ",  df$second_column) #Alternativ

#Split at "   " and unlist result
. <- unlist(strsplit(., "   ", fixed=TRUE))
#. <- sub("   ", "\n", ., fixed=TRUE) #Alternativ

#use read.table to get columns
read.table(text = .)
#  V1 V2
#1  1  2
#2  3  4
#3  5  6
#4  7  8
#5  9 10
#6 11 12

或使用trimws

. <- trimws(df$second_column, whitespace = "[][]")

. <- unlist(strsplit(., "],[", fixed=TRUE))
#. <- sub("],[", "\n", ., fixed=TRUE) #Alternativ

read.csv(text=.)

评论

0赞 94621 3/8/2023
在此解决方案中,生成的表具有较长的布局:当 CSV 有 10 行且嵌套表的大小为 10x10 时,生成的表将为 100 x 10(其中 x 10 是嵌套数组中的列数)
0赞 GKi 3/8/2023
您能举个例子和该案例的预期输出吗?
0赞 94621 3/8/2023
没关系,这种形式最适合我。由于它是一系列观察结果,因此我在单独的 df 中提取了它,然后通过(提取的 df)进一步过滤了一个实体,但我在这里没有要求这样做obs[seq(1,nrow(obs),10,]obs.
0赞 ThomasIsCoding 3/8/2023 #5

来自的技巧py_evalreticulate

library(reticulate)
with(
  df,
  as.data.frame(
    do.call(
      rbind,
      py_eval(gsub("]], [[", "],[",
        toString(second_column),
        fixed = TRUE
      ))
    )
  )
)

或另一个技巧fromJSONjsonlite

library(jsonlite)
with(
  df,
  as.data.frame(
    fromJSON(sprintf("[%s]", gsub("]], [[", "],[",
      toString(second_column),
      fixed = TRUE
    )))[1, , ]
  )
)

输出

  V1 V2
1  1  2
2  3  4
3  5  6
4  7  8
5  9 10
6 11 12