【问题标题】:Combining multiple csv files in bash, splitting the lines into different rows在bash中组合多个csv文件,将行分成不同的行
【发布时间】:2021-04-08 08:46:25
【问题描述】:

我已经获得了 >100 个csv 文件,每个文件包含 >1000 个测量值,其结构类似于以下两个示例文件

MR44825_radiomics_MCA.csv

Case-1_Image: MR44825_head.nii.gz
Case-1_diagnostics_Configuration_EnabledImageTypes: {'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}}
Case-1_diagnostics_Image-original_Mean: -917.2822725885565

MR47987_radiomics_MCA.csv

Case-1_Image: MR47987_head.nii.gz
Case-1_diagnostics_Configuration_EnabledImageTypes: {'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}}
Case-1_diagnostics_Image-original_Mean: -442.31589128260026

标签总是一些不同长度的字符串,与度量不同的总是第一个:。每个测量包含相同的标签。测量值本身可能包含,,但相关值由{} 括起来。

现在我想合并这些文件,最好使用bash。输出csv 的结构应如下所示:

Case-1_Image,Case-1_diagnostics_Configuration_EnabledImageTypes,Case-1_diagnostics_Image-original_Mean
MR44825_head.nii.gz,{'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}},-917.2822725885565
MR47987_head.nii.gz,{'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}},-442.31589128260026

【问题讨论】:

  • 我们鼓励提问者展示他们迄今为止为自己解决问题所做的尝试。
  • 您的问题中没有 CSV 输入文件。请edit您的问题包含minimal reproducible example,其中包含简洁、可测试的样本输入(至少 2 个 CSV,因为您正在寻求帮助以合并多个文件)以及给定该输入的预期输出。
  • 因为您的数据字段 (json) 中有逗号,所以这在最后将不起作用。

标签: bash csv awk


【解决方案1】:

假设:

  • OP 有理由使用非 JSON 格式(并且 OP 可以使用逗号 (,) 作为分隔符和数据)
  • 所有源文件的行数都相同
  • 任何源文件中都没有空行
  • 所有源文件在第一个:之前都有相同的标签
  • 所有源文件的标签顺序相同
  • 标签的数量和拼写是未知的(即,我们需要动态解析、存储和打印标签)

一个awk想法:

注意:由于需要动态处理标签,有点冗长

awk '
BEGIN  { split("",hdr)                        # declare hdr as an array
         split("",data)                       # declare data as an array
         ndx=1                                # init array index
       }

function print_row() {                        # function to print a row

pfx=""                                        # first column will have a NULL prefix

if ( length(hdr) > 0  )                       # print the header row?
   { for ( i in hdr )
       { printf "%s%s", pfx, hdr[i]
         pfx=","                              # 2nd-nth columns will have a "," prefix
       }
     printf "\n"
     split("",hdr)                            # clear hdr[] array so we do not print it again
   }

pfx=""                                        # reset prefix for printing data row

if ( length(data) > 0 )                       # print a data row?
   { for ( i in data )
         { printf "%s%s", pfx, data[i]
           pfx=","                            # 2nd-nth columns will have a "," prefix
         }
     printf "\n"
     split("",data)                           # clear the data[] array for the next file
     ndx=1                                    # reset our array index for the next file
   }
}

FNR==1 { print_row() }                        # if this is a new file then print contents of last file

       { if ( FNR==NR )                       # if this is the first file then make sure to populate the hdr[] array
            hdr[ndx]=gensub(/:$/,"","g",$1)   # strip trailing ":" from field #1; store in hdr[] array
         $1=""                                # clear field #1
         data[ndx]=gensub(/^ /,"","g",$0)     # strip leading " " from the line; store in data[] array
         ndx++                                # increment array index
         next
       }

END    { print_row() }                        # flush last set of data[] to stdout

' MR*MCA.csv

当针对 2x 样本数据文件运行时,会生成:

Case-1_Image,Case-1_diagnostics_Configuration_EnabledImageTypes,Case-1_diagnostics_Image-original_Mean
MR44825_head.nii.gz,{'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}},-917.2822725885565
MR47987_head.nii.gz,{'Original': {}, 'LoG': {'sigma': [2.0, 4.0, 6.0]}, 'Wavelet': {}},-442.31589128260026

【讨论】:

  • 首先非常感谢您在这里投入的时间和精力!除了最后一个,您的假设都是正确的。我知道所有标签的数量和拼写,如果有帮助可以提供。由于我是一般编码方面的新手,尤其是awk,我可能需要一些时间来实现这一点,然后才能给你反馈。简化执行您在几个测试csv 文件的文件夹中提供的代码会为我产生以下错误:
  • awk: line 7: illegal reference to variable hdr awk: line 8: illegal reference to variable hdr awk: line 12: illegal reference to variable hdr awk: line 18: illegal reference to variable data awk: line 19: illegal reference to variable data
  • hmmmm ... 在您的系统上运行awk --version 的输出是什么?如果这些是唯一的错误消息,它们都来自函数内部的数组引用;我想知道您的awk 版本是否需要脚本顶部的预声明步骤?我已经编辑了答案,将BEGIN 块移到顶部,因此数组预声明现在发生在函数定义之前。
  • awk --version 不起作用,导致awk: not an option: --versionawk -W version 给出了一个结果,mawk 1.3.3 Nov 1996, Copyright (C) Michael D. Brennan。那么更新mawk 可能会带来解决方案?
  • 运行包含BEGIN的编辑代码会导致以下错误:awk: 2: unexpected character 0xc2 awk: 2: unexpected character 0xc2 awk: 3: unexpected character 0xc2 awk: 4: unexpected character 0xc2 awk: 5: unexpected character 0xc2
【解决方案2】:

CSV 对于您提供的数据并没有真正意义,而您说想要作为输出的伪 CSV 几乎没有意义,并且难以进一步处理。也许将每个输入文件转换为 JSON 会更有意义,并允许使用标准工具进行处理。

awk -F ': ' 'FNR==1 { name=$2 }
    FNR==2 { j = substr($0, length($1)+3); gsub(/\047/, "\042", j) }
    FNR==3 { sub(/^{/, "{\042name\042: \042" name "\042,", j);
        sub(/}$/, ",\042mean\042: " $2 "}", j);
        print j }' *.csv >output.jsonl

输出应该是这样的

{"name":"MR20584_head.nii.gz","Original": {}, "LoG": {"sigma": [2.0, 4.0, 6.0]}, "Wavelet": {},"mean": -917.2822725885565}
{"name":"MR30211_head.nii.gz","Original": {}, "LoG": {"sigma": [2.0, 4.0, 6.0]}, "Wavelet": {},"mean":-1024.287275914652}

这种格式是 JSON 行,即每一行都是有效的 JSON,但文件本身不是正确的 JSON。

演示:https://ideone.com/ue0Crd

当然,如果你能修复生成这种无用格式的工具,那就更好了。

【讨论】:

  • 不知道为什么使用\042 比使用\" 更好。
  • 我想这并没有更好本身;你认为它更糟吗?
  • 就我个人而言,它只是让代码更难阅读,我需要几秒钟来提醒/说服自己 \042" 然后我花时间试图弄清楚是什么导致代码需要\042s 而不仅仅是它们所代表的字符,然后最终得出没有实际理由的结论,所以对我来说\042"\" 要差,但我想是YMMV。不过,沿着这条路走下去——我们可以用 ASCII 转义序列替换哪些其他字符……可以制作一些非常有趣的脚本 :-)。
  • 是的,我完全可以欣赏一个模糊的 Aw 上下文......大胆地将 Awk 带到 sed 自从人们停止编写 TECO 宏以来一直占据主导地位的地方。
猜你喜欢
  • 1970-01-01
  • 2012-12-08
  • 2018-08-11
  • 2020-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多