【问题标题】:i want to merge multiple csv files by specific condition using perl我想使用 perl 按特定条件合并多个 csv 文件
【发布时间】:2013-04-03 22:56:42
【问题描述】:

我有多个 csv 文件,我想合并所有这些文件..... 我在下面展示了我的一些示例 csv 文件...

M1DL1_Interpro_sum.csv

IPR017690,Outer membrane, omp85 target,821
IPR014729,Rossmann,327
IPR013785,Aldolase,304
IPR015421,Pyridoxal,224
IPR003594,ATPase,179
IPR000531,TonB receptor,150
IPR018248,EF-hand,10

M1DL2_Interpro_sum.csv

IPR017690,Outer membrane, omp85 target,728
IPR013785,Aldolase,300
IPR014729,Rossmann,261
IPR015421,Pyridoxal,189
IPR011991,Winged,113
IPR000873,AMP-dependent synthetase/ligase,111

M1DL3_Interpro_sum.csv

IPR017690,Outer membrane,905
IPR013785,Aldolase,367
IPR014729,Rossmann,338
IPR015421,Pyridoxal,271
IPR003594,ATPase,158
IPR018248,EF-hand,3

现在要合并这些文件,我尝试了以下代码

@ARGV = <merge_csvfiles/*.csv>;
print @ARGV[0],"\n";
open(PAGE,">outfile.csv") || die"Can't open outfile.csv\n";
while($i<scalar(@ARGV))
{
open(FILE,@ARGV[$i]) || die"Can't open ...@ARGV[$i]...\n";
$data.=join("",<FILE>);

close FILE;
print"file completed...",$i+1,"\n";
$i++;
}


@data=split("\n",$data);
@data2=@data;

print scalar(@data);

for($i=0;$i<scalar(@data);$i++) 
{
@id1=split(",",@data[$i]);
$id_1=@id1[0];
@data[$j]=~s/\n//;
if(@data[$i] ne "")
{
    print PAGE "\n@data[$i],";
    for($j=$i+1;$j<scalar(@data2);$j++)
    {
        @id2=split(",",@data2[$j]);
        $id_2=@id2[0];
        if($id_1 eq $id_2)
        {

            @data[$j]=~s/\n//;
            print PAGE "@data2[$j],";
            @data2[$j]="";
            @data[$j]="";
            print "match found at ",$i+1," and ",$j+1,"\n";
        }
    }
}


print $i+1,"\n";
}

merge_csvfiles 是一个包含所有文件的文件夹

以上代码的输出是

IPR017690,Outer membrane,821,IPR017690,Outer membrane  ,728,IPR017690,Outer membrane,905
IPR014729,Rossmann,327,IPR014729,Rossmann,261,IPR014729,Rossmann,338
IPR013785,Aldolase,304,IPR013785,Aldolase,300,IPR013785,Aldolase,367
IPR015421,Pyridoxal,224,IPR015421,Pyridoxal,189,IPR015421,Pyridoxal,271
IPR003594,ATPase,179,IPR003594,ATPase,158
IPR000531,TonB receptor,150
IPR018248,EF-hand,10,IPR018248,EF-hand,3
IPR011991,Winged,113
IPR000873,AMP-dependent synthetase/ligase

但我想要以下格式的输出....

IPR017690,Outer membrane,821,IPR017690,Outer membrane  ,728,IPR017690,Outer membrane,905
IPR014729,Rossmann,327,IPR014729,Rossmann,261,IPR014729,Rossmann,338
IPR013785,Aldolase,304,IPR013785,Aldolase,300,IPR013785,Aldolase,367
IPR015421,Pyridoxal,224,IPR015421,Pyridoxal,189,IPR015421,Pyridoxal,271
IPR003594,ATPase,179,0,0,0,IPR003594,ATPase,158
IPR000531,TonB receptor,150,0,0,0,0,0,0
IPR018248,EF-hand,10,0,0,0,IPR018248,EF-hand,3
0,0,0,IPR011991,Winged,113,0,0,0
0,0,0,IPR000873,AMP-dependent synthetase/ligase,111,0,0,0

有人知道我该怎么做吗? 谢谢你的帮助

【问题讨论】:

  • 您能解释一下您的上下文中的“合并”是什么意思吗?
  • Miguel Prz 我已经编辑了这个问题...我希望你现在清楚我所说的合并是什么意思

标签: perl csv


【解决方案1】:

正如 Miguel Prz 的评论中提到的,您没有解释您希望如何执行合并,但是,从“期望的输出”示例来看,您想要的似乎是连接所有具有匹配 ID 的行将三个输入文件合并到输出文件的一行中,用“0,0,0”代替给定文件中未出现的任何行。

那么,那么:

#!/usr/bin/env perl    

use strict;
use warnings;

my @input_files = glob 'merge_csvfiles/*.csv';
my %data;
for my $i (0 .. $#input_files) {
  open my $infh, '<', $input_files[$i]
    or die "Failed to open $input_files[$i]: $!";
  while (<$infh>) {
    chomp;
    my $id = (split ',', $_, 2)[0];
    $data{$id}[$i] = $_;
  }
  print "Input file read: $input_files[$i]\n";
}

open my $outfh, '>', 'outfile.csv' or die "Failed to open outfile.csv: $!";
for my $id (sort keys %data) {
  my @merge_data;
  for my $i (0 .. $#input_files) {
    push @merge_data, $data{$id}[$i] || '0,0,0';
  }
  print $outfh join(',', @merge_data) . "\n";
}

第一个循环将每个文件中的所有行收集到一个数组哈希中。哈希键是 ID,因此所有文件中该 ID 的行保持在一起,每个键的值是(引用)每个文件中与该 ID 关联的行的数组;为此使用数组可以让我们跟踪丢失的值以及存在的值。

然后第二个循环获取该哈希的键(按字母顺序),并为每个键创建一个与该 ID 关联的值的临时数组,用“0,0,0”替换缺失值,将它们连接起来成单个字符串,并将其打印到输出文件。

outfile.csv 中的结果是:

IPR000531,TonB receptor,150,0,0,0,0,0,0
0,0,0,IPR000873,AMP-dependent synthetase/ligase,111,0,0,0
IPR003594,ATPase,179,0,0,0,IPR003594,ATPase,158
0,0,0,IPR011991,Winged,113,0,0,0
IPR013785,Aldolase,304,IPR013785,Aldolase,300,IPR013785,Aldolase,367
IPR014729,Rossmann,327,IPR014729,Rossmann,261,IPR014729,Rossmann,338
IPR015421,Pyridoxal,224,IPR015421,Pyridoxal,189,IPR015421,Pyridoxal,271
IPR017690,Outer membrane, omp85 target,821,IPR017690,Outer membrane, omp85 target,728,IPR017690,Outer membrane,905
IPR018248,EF-hand,10,0,0,0,IPR018248,EF-hand,3

编辑:在 cmets 中添加了 OP 要求的解释

你能解释一下我的 $id = (split ',', $_, 2)[0]; 的工作原理吗?和 $# 在这个程序中

my $id = (split ',', $_, 2)[0]; 获取已读取文本的最后一行中第一个逗号之前的文本:

  • 由于我没有指定将数据放入哪个变量,while (&lt;$infh&gt;) 将其读入默认变量$_
  • split ',', $_, 2$_ 的值拆分为逗号分隔的字段列表。最后的2 告诉它最多只产生2个字段;没有2,代码也可以正常工作,但是,由于我只需要第一个字段,因此不需要拆分为更多部分。
  • (...)[0] 放在split 命令周围会将返回的字段列表转换为(匿名)数组并返回该数组的第一个元素。就像我写的 my @fields = split ',', $_, 2; my $id = $fields[0]; 一样,但更短且没有额外的变量。

$#array 返回数组@array 中编号最高的索引,因此for my $i (0 .. $#array) 仅表示“循环遍历@array 中所有元素的索引”。 (请注意,如果我不需要索引计数器的值,我会直接使用 for my $filename (@input_files) 循环遍历数组的数据,但如果我跟踪缺失值会不太方便会那样做的。)

【讨论】:

  • 非常感谢...但是你能解释一下我的 $id = (split ',', $_, 2)[0];和 $# 在这个程序中,因为我是 perl 的新手......谢谢你
  • @user2181315:我已经编辑了我的答案,以添加对您询问的内容的解释。
猜你喜欢
  • 2013-08-02
  • 2012-11-16
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 2021-04-28
  • 2021-09-27
  • 2021-04-25
相关资源
最近更新 更多