从两个不同的 csv 文件中提取具有相似名称的列

从两个不同的 csv 文件中提取具有相似名称的列

我有两个不同的 csv 文件,一个文件包含扩展列名称,而另一个文件包含相同列名称的快捷方式。

例如 :

csv 文件 1 是:

gender,aciclovir drug,aclidinium bromide abc,acenocoumarol drdd
male,2008,2009,2009

csv 文件 2 是:

gender,aciclovir,aclidinium bromide,ajmaline
male,2008,2009,2010

现在我想从文件 1 中提取与文件 2 中的列有共同词的列。

在我的示例中放置所需的输出:

gender,aciclovir drug,aclidinium bromide abc
male,2008,2009

答案1

假设两个文件中的标头都是以逗号分隔的字段名称的简单列表,没有任何特殊的 CSV 引用,我们可以使用以下命令从文件中的不同行中提取字段名称

head -n 1 file | tr , '\n'

或者,与sed,

sed -e 'y/,/\n/' -e q file

进一步假设所有字段名称都不包含正则表达式中的特殊字符(.[*\^(在字段名称的开头)和$(在字段名称的结尾)),我们可以使用它来创建其中的一组正则表达式file2将匹配字段名称的开头file1^在每行的开头插入将执行此操作):

head -n 1 file2 | tr , '\n' | sed 's/^/^/'

或者,与sed,

sed -e 'y/,/\n/' -e q file2 | sed 's/^/^/'

或者,通过一次sed调用,

sed -e 'y/,/\n/' -e 's/^/^/' -e 's/\n/&^/g' -e q file2

或者,使用 GNU sed

sed -e 's/^/^/' -e 's/,/\n^/g' -e q file2

我们可以将这些表达式应用于字段名称列表,以从file1该文件中获取应提取的字段名称:

grep -f <( head -n 1 file2 | tr , '\n' | sed 's/^/^/' ) <( head -n 1 file1 | tr , '\n' )

鉴于您在问题中显示的数据,其结果将是以下列表:

gender
aciclovir drug
aclidinium bromide abc

请注意,如果 中的字段名称与file2中的多个字段名称匹配,则此列表可能包含重复条目file1

然后,我们可以创建这些字段名称的逗号分隔列表,并将其用作cut子命令的参数磨坊主最终从中提取所需的字段file1

mlr --csv cut -f "$( grep -f <( head -n 1 file2 | tr , '\n' | sed 's/^/^/' ) <( head -n 1 file1 | tr , '\n' ) | tr '\n' , )" file1

这会给我们

gender,aciclovir drug,aclidinium bromide abc
male,2008,2009

答案2

使用(以前称为 Perl_6)

raku -MText::CSV -e '  \

  #read headers of each csv, match column names on first word;
      my $csvA = Text::CSV.new; my $csvB = Text::CSV.new; 
      my $fhA = "csv1.csv".IO.open;  my $fhB = "csv2.csv".IO.open; 
      my @hdrA = $csvA.header($fhA).column-names;  my @hdrB = $csvB.header($fhB).column-names; 
      my %fld = @hdrA.map(*.words[0]) (&) @hdrB.map(*.words[0]);  
      close $fhA; close $fhB;

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

  #output array that has been @whole>>.[index] filtered for desired columns;
     .join(",").put for @whole>>.[@hdrA.map(*.words[0]).grep(/@(%fld.keys)/, :k)];'

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

上面的外部模块Text::CSV用于解析 CSV。文件句柄和Text::CSV.new()对象一样被定义。对该$csvA对象进行操作,以便将header文件句柄的第一行 ( )$fhA解析为column-names并存储为@hdrA.类似地,$csvB操作该对象,使得header文件句柄的第一行 ( )$fhB被解析为column-names并存储为@hdrB.

为了获得“交集”,@使用 ASCII“set-intersection”运算符将每个 -sigiled 标头数组与其他数组进行比较(&)。如果您更喜欢此处的 Unicode 符号,则可以使用它。交集被存储为%fld散列。

请注意,在比较(交集)行中,您可以通过将每个数组元素拆分为 来更改匹配条件words,然后[0]测试每列第一个单词的匹配,而不是获得精确匹配。结果是三个匹配的列:

输入示例:

#csv1.csv
gender,aciclovir drug,aclidinium bromide abc,acenocoumarol drdd
male,2008,2009,2009

#csv2.csv
gender,aciclovir,aclidinium bromide,ajmaline
male,2008,2009,2010

示例输出(未引用):

gender,aciclovir drug,aclidinium bromide abc
male,2008,2009

示例输出(双引号,添加>>.raku到上面代码最后一行的最末尾):

"gender","aciclovir drug","aclidinium bromide abc"
"male","2008","2009"

如果需要输出csv,下面的markdown文档是这样指示的:

# and write CSV file, filtered as above
my $fh_out = open "new.csv", :w;
$csv.say($fh_out, $_) for @whole>>.[@hdrA.map(*.words[0]).grep(/@(%fld.keys)/, :k)];
$fh_out.close;

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

相关内容