如何从 CSV 文件中提取名称包含单词“chronic”的列

如何从 CSV 文件中提取名称包含单词“chronic”的列

我有一个大的 csv 文件(大约 1000 列),我想仅将标题名称中包含“慢性”一词的列提取到新文件中。我怎样才能做到这一点 ?

例如,如果我有:

gender,chronic_disease1,chronic_disease2
male,2008,2009

期望的输出是:

chronic_disease1,chronic_disease2
2008,2009

注意:列/字段分隔符为逗号“,”。如果没有chronic匹配则根本没有输出。

答案1

使用磨坊主(可从 Ubuntu“universe”存储库获取)其cut动词可以选择使用正则表达式来匹配字段名称:

mlr --csv cut -r -f 'chronic' file.csv

chronic(匹配字段名称中任意位置的子字符串)或更具体地说

mlr --csv cut -r -f '^chronic_' file.csv

(将子字符串锚定到名称的开头,并添加尾随下划线)或

mlr --csv cut -r -f '"^chronic_"i' file.csv

使后者不区分大小写地匹配。

要反转匹配,即选择所有列不是匹配^chronic_,添加-x

mlr --csv cut -x -r -f '"^chronic_"i' file.csv

--csvlite注意:如果您的输入文件不包含更高级的 CSV 功能(例如 RFC-4180 样式双引号),您也许能够使用更高效的引擎。看文件格式 - CSV/TSV/ASV/USV/等。

如果没有包含该字符串的字段名称chronic,并且您根本不希望输出而不是空记录,请通过 Miller 的skip-trivial-records子命令传递提取的数据。

mlr --csv cut -r -f 'chronic' then skip-trivial-records file.csv

答案2

使用 awk:

awk '
  BEGIN{ FS=OFS="," }
  NR==1{
         for(i=1; i<=NF; i++)
             found+=col[i]=($i ~ /chronic/)
         if(!found) exit
       }
  {
    for(i=1; i<=NF; i++)
        printf ("%s", (col[i]? (c++?OFS:"")$i :"") )
    printf("%s", (c?"\n" : "") ); c=0
  }' infile.csv

我们将Field Separator和Output Field Separator设置为逗号,表示输入文件是CSV文件。

对于第一个输入行(假设是标题行),我们创建一个数组col[]来存储该行中的每个字段是否包含子字符串“慢性的" 然后 TRUE/1(通过将每个字段与/chronic/正则表达式匹配)或 FALSE/0(如果不匹配)。

if(!found) exit如果没有任何字段要输出,这部分代码告诉 awk 退出命令并停止处理输入文件。否则...

...然后对于每个后续行(以及第一行),它循环遍历该行中的每个字段,如果相应的col[i]值为 1,则打印该字段,否则打印一个空字符串;处理该行后,如果有任何字段输出(当c计数器非零时;c计数器也用于在输出时不是第一个字段时在字段之间添加 OFS),它会打印换行符,否则不打印任何内容,并重置c为0。

答案3

假设字段名称位于 .csv 文件的第一行,如下所示:

$ cat input.csv 
gender,chronic_disease1,chronic_disease2
male,2008,2009

以下 perl 单行代码将打印字段名称包含字符串“chronic”的字段:

perl -F, -lane '
  if ($. == 1) {   # first line of input
    # get a list of field numbers & names matching "chronic"
    foreach my $f (0..$#F) {
      if ($F[$f] =~ /chronic/i) { # case-insensitive 
        push @out, $f;            # get the field numbers
        push @outnames, $F[$f];   # get the names too
      }
    };
    last unless (@out);           # exit early if there's nothing to print
  } else {
    print join(",", @outnames) if ($. == 2); # print the header only once
    print join(",", @F[@out])                # print the data
  }' input.csv 

示例输出:

chronic_disease1,chronic_disease2
2008,2009

注意:这仅适用于简单的逗号分隔文件。它不适用于包含嵌入逗号或换行符的引用字段的 CSV 文件。为此,您需要使用 CSV 解析器 - 例如 perl 的文本::CSV,甚至是 Perl 的DBD::CSV模块用于数据库接口它允许您对 CSV 文件执行 SQL 查询,就像它们是 SQL 数据库一样。或者使用磨坊主

答案4

使用(以前称为 Perl6)

~$ raku -MText::CSV -e '  \

  #read header into @hdr array
      my $csv1 = Text::CSV.new;
      my $fh1 = "chronic_test.txt".IO.open;
      my @hdr = $csv1.header($fh1, munge-column-names => "fc").column-names;
      close $fh1;

  #read full csv file into @whole array
      my $csv2 = Text::CSV.new;
      my $fh2 = "chronic_test.txt".IO.open; 
      my @whole; while $csv2.getline($fh2) -> $row {
      @whole.push: $row;
      }; close $fh2;

  #output array that has been @whole>>.[index] filtered for desired columns
     .join(",").put for @whole>>.[@hdr.grep(/chronic/, :k)];'

输入示例:

gender,chronic_disease1,chronic_disease2
male,2008,2009

示例输出:

chronic_disease1,chronic_disease2
2008,2009

Raku 是 Perl 编程语言家族中的一种语言。它具有对 Unicode 的高级支持和强大的正则表达式实现。

Raku的Text::CSV模块解析有效的CSV,并可以输出有效的CSV。如果您需要接受备用列分隔符(例如制表符),或者如何处理引用字段、空白字段、嵌入换行符和/或逗号等,请检查下面的 Markdown 文档。

以上是按列名读取/过滤 CSV 文件的相当强大(但冗长)的方法。简而言之,标头被读取两次,并使用正则表达式来grep输出匹配的列。您可以munge根据需要将列名称切换为其他大小写(uclcfc等)。

底部的markdown文档提供了以下代码来输出CSV文件(修改为仅输出所需的列):

# and write CSV file, filtered as above
my $fh_out = open "new.csv", :w;
$csv.say($fh_out, $_) for @whole>>.[@hdr.grep(/chronic/, :k)];
$fh_out.close;


更有效:请注意,上面的代码实际上将@wholecsv 文件读取到内存中,尽管是逐行读取的。下面的代码仅将@filteredcsv 列读入内存,因此内存效率可能更高。

$注意:“推广” -sigiled非常重要标量@-sigiled大批当使用这样的对象作为“位置索引”。促销可以是以下形式@($index)或更简单@$index

~ % raku -MText::CSV -e '  \

  #read header into @hdr array
      my $csv1 = Text::CSV.new;
      my $fh1 = "chronic_test.txt".IO.open;
      my @hdr = $csv1.header($fh1, munge-column-names => "fc").column-names;
      my $index = @hdr.grep(/chronic/, :k); close $fh1;

  #read filtered csv file into @filtered array
      my $csv2 = Text::CSV.new;
      my $fh2 = "chronic_test.txt".IO.open;
      my @filtered; while $csv2.getline($fh2) -> $row {
      @filtered.push: $row.[@$index];
      }; close $fh2;

     .join(",").put for @filtered;'

https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md
https://docs.raku.org
https://raku.org

相关内容