我有两个不同的 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