gpt4 book ai didi

Perl 用主键逐行合并 2 个 csv 文件

转载 作者:行者123 更新时间:2023-12-04 14:20:02 25 4
gpt4 key购买 nike

编辑:添加了解决方案。

嗨,我目前有一些工作虽然很慢的代码。

它合并 2 CSV 使用主键逐行文件。
例如,如果文件 1 有以下行:

"one,two,,four,42"

文件 2 有这一行;
"one,,three,,42"

其中 0 索引 $position = 4 的主键 = 42;

然后子:merge_file($file1,$file2,$outputfile,$position);

将输出带有以下行的文件:
"one,two,three,four,42";

每个主键在每个文件中都是唯一的,一个键可能存在于一个文件中,但不存在于另一个文件中(反之亦然)

每个文件大约有 100 万行。

遍历第一个文件中的每一行,我使用散列来存储主键,并将行号存储为值。行号对应一个数组[line num],它存储第一个文件中的每一行。

然后我检查第二个文件中的每一行,并检查主键是否在哈希中,如果是,从 file1array 中获取该行,然后将我需要的列从第一个数组添加到第二个数组,然后然后连接。到最后。然后删除哈希,然后在最后,将整个内容转储到文件中。 (我使用的是 SSD,所以我想尽量减少文件写入。)

最好用代码来解释:
sub merge_file2{
my ($file1,$file2,$out,$position) = ($_[0],$_[1],$_[2],$_[3]);
print "merging: \n$file1 and \n$file2, to: \n$out\n";
my $OUTSTRING = undef;

my %line_for;
my @file1array;
open FILE1, "<$file1";
print "$file1 opened\n";
while (<FILE1>){
chomp;
$line_for{read_csv_string($_,$position)}=$.; #reads csv line at current position (of key)
$file1array[$.] = $_; #store line in file1array.
}
close FILE1;
print "$file2 opened - merging..\n";
open FILE2, "<", $file2;
my @from1to2 = qw( 2 4 8 17 18 19); #which columns from file 1 to be added into cols. of file 2.
while (<FILE2>){
print "$.\n" if ($.%1000) == 0;
chomp;
my @array1 = ();
my @array2 = ();
my @array2 = split /,/, $_; #split 2nd csv line by commas

my @array1 = split /,/, $file1array[$line_for{$array2[$position]}];
# ^ ^ ^
# prev line lookup line in 1st file,lookup hash, pos of key
#my @output = &merge_string(\@array1,\@array2); #merge 2 csv strings (old fn.)

foreach(@from1to2){
$array2[$_] = $array1[$_];
}
my $outstring = join ",", @array2;
$OUTSTRING.=$outstring."\n";
delete $line_for{$array2[$position]};
}
close FILE2;
print "adding rest of lines\n";
foreach my $key (sort { $a <=> $b } keys %line_for){
$OUTSTRING.= $file1array[$line_for{$key}]."\n";
}

print "writing file $out\n\n\n";
write_line($out,$OUTSTRING);
}

第一个 while 很好,不到 1 分钟,但是第二个 while 循环需要大约 1 小时才能运行,我想知道我是否采取了正确的方法。我认为有可能进行很多加速吗? :) 提前致谢。

解决方案:
sub merge_file3{
my ($file1,$file2,$out,$position,$hsize) = ($_[0],$_[1],$_[2],$_[3],$_[4]);
print "merging: \n$file1 and \n$file2, to: \n$out\n";
my $OUTSTRING = undef;
my $header;

my (@file1,@file2);
open FILE1, "<$file1" or die;
while (<FILE1>){
if ($.==1){
$header = $_;
next;
}
print "$.\n" if ($.%100000) == 0;
chomp;
push @file1, [split ',', $_];
}
close FILE1;

open FILE2, "<$file2" or die;
while (<FILE2>){
next if $.==1;
print "$.\n" if ($.%100000) == 0;
chomp;
push @file2, [split ',', $_];
}
close FILE2;

print "sorting files\n";
my @sortedf1 = sort {$a->[$position] <=> $b->[$position]} @file1;
my @sortedf2 = sort {$a->[$position] <=> $b->[$position]} @file2;
print "sorted\n";
@file1 = undef;
@file2 = undef;
#foreach my $line (@file1){print "\t [ @$line ],\n"; }

my ($i,$j) = (0,0);
while ($i < $#sortedf1 and $j < $#sortedf2){
my $key1 = $sortedf1[$i][$position];
my $key2 = $sortedf2[$j][$position];
if ($key1 eq $key2){
foreach(0..$hsize){ #header size.
$sortedf2[$j][$_] = $sortedf1[$i][$_] if $sortedf1[$i][$_] ne undef;
}
$i++;
$j++;
}
elsif ( $key1 < $key2){
push(@sortedf2,[@{$sortedf1[$i]}]);
$i++;
}
elsif ( $key1 > $key2){
$j++;
}
}

#foreach my $line (@sortedf2){print "\t [ @$line ],\n"; }

print "outputting to file\n";
open OUT, ">$out";
print OUT $header;
foreach(@sortedf2){
print OUT (join ",", @{$_})."\n";
}
close OUT;

}

谢谢大家,解决方法已经贴在上面了。现在合并整个内容大约需要 1 分钟! :)

最佳答案

我想到了两种技术。

  • 将 CSV 文件中的数据读入 DBMS 中的两个表(SQLite 可以正常工作),然后使用 DB 进行连接并将数据写回 CSV。数据库将使用索引来优化连接。
  • 首先,按主键对每个文件进行排序(使用 perl 或 unix sort),然后并行对每个文件进行线性扫描(从每个文件读取一条记录;如果键相等,则输出一个连接行并推进两个文件; 如果键不相等,则使用较小的键推进文件并重试)。这一步是 O(n + m) 时间而不是 O(n * m) 和 O(1) 内存。
  • 关于Perl 用主键逐行合并 2 个 csv 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3125951/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com