拆分巨大的 CSV 文件
Splitting huge CSV file
我有一个大约 20 GB 的巨大 csv 文件。它有 5,000 列和 2,500,000 行。我想将其中的每一列写入一个文件。我已经尝试过 FOR 循环,但速度很慢。我的代码如下:
Columns=$(head -n 1 train.csv | sed "s/,/\n/g" | wc -l)
mkdir cols
for i in `seq 1 $Columns`;
do
echo $i
tail -n +2 train.csv | cut -d',' -f$i > cols/col_$i.txt
done
我采纳任何建议来加速此过程。
这是一个 bash 脚本,它一次性完成:
Columns=$(head -n 1 train.csv | sed "s/,/\n/g" | wc -l)
mkdir cols
tail -n +2 train.csv | \
while IFS=, read -ra row; do
for i in `seq 1 $Columns`; do
echo "${row[$(($i-1))]}" >> cols/col_$i.txt
done
done
这个脚本的缺点是它会打开和关闭列文件数百万次。以下 perl 脚本通过保持所有文件打开来避免该问题:
#!/usr/bin/perl
use strict;
use warnings;
my @handles;
open my $fh,'<','train.csv' or die;
<$fh>; #skip the header
while (<$fh>) {
chomp;
my @values=split /,/;
for (my $i=0; $i<@values; $i++) {
if (!defined $handles[$i]) {
open $handles[$i],'>','cols/col_'.($i+1).'.txt' or die;
}
print {$handles[$i]} "$values[$i]\n";
}
}
close $fh;
close $_ for @handles;
由于您有 5000 列并且此脚本保持 5001 个文件打开,因此您需要增加系统允许您拥有的打开文件描述符的数量。
Perl 解决方案。它一次打开 1000 个文件,因此它会将您的输入传递 5 次。 运行 以输入文件名作为参数。
#!/usr/bin/perl
use warnings;
use strict;
my $inputfile = shift;
open my $input, '<', $inputfile or die $!;
mkdir 'cols';
my @headers = split /,/, <$input>;
chomp $headers[-1];
my $pos = tell $input; # Remember where the first data line starts.
my $step = 1000;
for (my $from = 0; $from <= $#headers; $from += $step) {
my $to = $from + $step - 1;
$to = $#headers if $#headers < $to;
warn "$from .. $to";
# Open the files and print the headers in range.
my @fhs;
for ($from .. $to) {
open $fhs[ $_ - $from ], '>', "cols/col-$_" or die $!;
print { $fhs[ $_ - $from ] } $headers[$_], "\n";
}
# Print the columns in range.
while (<$input>) {
chomp;
my $i = 0;
print { $fhs[$i++] } $_, "\n" for (split /,/)[ $from .. $to ];
}
close for @fhs;
seek $input, $pos, 0; # Go back to the first data line.
}
在 awk 中:
$ awk '{for(i=1;i<=NF;i++) print $i > i}' train.csv
生成5000个文件的测试版:
$ cat > foo
1
2
3
$ awk 'BEGIN {for(i=1;i<=5000;i++) a=a i (i<5000? OFS:"")} {[=11=]=a; for(i=1;i<=NF; i++) print $i > i}' foo
$ ls -l | wc -l
5002 # = 1-5000 + foo and "total 20004"
$ cat 5000
5000
5000
5000
它在我的笔记本电脑上持续了 250 行:
real 1m4.691s
user 1m4.456s
sys 0m0.180s
我有一个大约 20 GB 的巨大 csv 文件。它有 5,000 列和 2,500,000 行。我想将其中的每一列写入一个文件。我已经尝试过 FOR 循环,但速度很慢。我的代码如下:
Columns=$(head -n 1 train.csv | sed "s/,/\n/g" | wc -l)
mkdir cols
for i in `seq 1 $Columns`;
do
echo $i
tail -n +2 train.csv | cut -d',' -f$i > cols/col_$i.txt
done
我采纳任何建议来加速此过程。
这是一个 bash 脚本,它一次性完成:
Columns=$(head -n 1 train.csv | sed "s/,/\n/g" | wc -l)
mkdir cols
tail -n +2 train.csv | \
while IFS=, read -ra row; do
for i in `seq 1 $Columns`; do
echo "${row[$(($i-1))]}" >> cols/col_$i.txt
done
done
这个脚本的缺点是它会打开和关闭列文件数百万次。以下 perl 脚本通过保持所有文件打开来避免该问题:
#!/usr/bin/perl
use strict;
use warnings;
my @handles;
open my $fh,'<','train.csv' or die;
<$fh>; #skip the header
while (<$fh>) {
chomp;
my @values=split /,/;
for (my $i=0; $i<@values; $i++) {
if (!defined $handles[$i]) {
open $handles[$i],'>','cols/col_'.($i+1).'.txt' or die;
}
print {$handles[$i]} "$values[$i]\n";
}
}
close $fh;
close $_ for @handles;
由于您有 5000 列并且此脚本保持 5001 个文件打开,因此您需要增加系统允许您拥有的打开文件描述符的数量。
Perl 解决方案。它一次打开 1000 个文件,因此它会将您的输入传递 5 次。 运行 以输入文件名作为参数。
#!/usr/bin/perl
use warnings;
use strict;
my $inputfile = shift;
open my $input, '<', $inputfile or die $!;
mkdir 'cols';
my @headers = split /,/, <$input>;
chomp $headers[-1];
my $pos = tell $input; # Remember where the first data line starts.
my $step = 1000;
for (my $from = 0; $from <= $#headers; $from += $step) {
my $to = $from + $step - 1;
$to = $#headers if $#headers < $to;
warn "$from .. $to";
# Open the files and print the headers in range.
my @fhs;
for ($from .. $to) {
open $fhs[ $_ - $from ], '>', "cols/col-$_" or die $!;
print { $fhs[ $_ - $from ] } $headers[$_], "\n";
}
# Print the columns in range.
while (<$input>) {
chomp;
my $i = 0;
print { $fhs[$i++] } $_, "\n" for (split /,/)[ $from .. $to ];
}
close for @fhs;
seek $input, $pos, 0; # Go back to the first data line.
}
在 awk 中:
$ awk '{for(i=1;i<=NF;i++) print $i > i}' train.csv
生成5000个文件的测试版:
$ cat > foo
1
2
3
$ awk 'BEGIN {for(i=1;i<=5000;i++) a=a i (i<5000? OFS:"")} {[=11=]=a; for(i=1;i<=NF; i++) print $i > i}' foo
$ ls -l | wc -l
5002 # = 1-5000 + foo and "total 20004"
$ cat 5000
5000
5000
5000
它在我的笔记本电脑上持续了 250 行:
real 1m4.691s
user 1m4.456s
sys 0m0.180s