以下愚蠢的硬编码应该是某种循环或并行构造,名义上起作用,但它是糟糕的mawk语法。我的好mawk语法尝试都失败了,在mawk(未显示)和gnu parallel(未显示)中使用for循环。
它确实需要从磁盘读取CSV文件一次,而不是每列读取一次,因为我有一个非常大的CSV文件(数百万行,数千列)。我的原始代码工作正常(未显示),但它为每一列再次读取整个磁盘文件,这需要几个小时,我在意识到发生了什么后将其杀死。我有一个使用GPU连接器插槽的快速固态磁盘,因此该设备上的磁盘读取速度非常快。因此CPU是这里的瓶颈。如果我必须硬编码4000行除了列号之外基本相同的语句,那么代码愚蠢就更是瓶颈了。
代码按列进行计数非数字值。我需要一些循环(for-loop)或并行(首选),因为虽然以下在2列上正常工作,但它不是一种为数千列编写mawk代码的可扩展方法。
tail -n +1 pht.csv | awk -F"," '(($1+0 != $1) && ($1!="")){cnt1++}; (($2+0 != $2) && ($2!="")){cnt2++} END{print cnt1+0; print cnt2+0}'
2
1
"第1列如何处理;第2栏处理;"重复代码减少了吗?如何引入循环?如何引入gnu parallel?非常感谢。我是awk的新手。对其他语言来说并不陌生。
我一直期待以下一个或多个bash命令的一些聪明的组合能够轻松地解决这个问题,但是这里我很多小时后没有任何东西要显示。我伸出双手来。代码贫乏的施舍?
抱歉,我绝对不需要使用像python pandas或R数据帧这样的CSV特定库。我的双手绑在这里。抱歉。谢谢你这么酷。在这种情况下我只能使用bash命令行。
我的mawk可以处理32000+列,所以NF在这里不是问题,不像我见过的其他awk。我的列少于32000(但不是那么多)。
Datafile pht.csv包含以下3x2数据集:
cat pht.csv
8,T1,
T13,3,T1
T13,,-6.350818276405334473e-01
答案 0 :(得分:3)
无法访问mawk
,但您可以执行与此相同的操作
awk -F, 'NR>1 {for(i=1;i<=NF;i++) if($i~/[[:alpha:]]/) a[i]++}
END {for(i=1;i in a;i++) print a[i]}' file
即使是百万条记录,也不会花费超过几分钟的时间。
为了识别指数表示法,正则表达式测试不起作用,您需要恢复到评论中提到的$1+0!=$1
测试。请注意,您不必单独检查空字符串。
答案 1 :(得分:3)
到目前为止,所有解决方案都没有并行化。让我们改变它。
假设您有一个串行工作的解决方案,可以从管道中读取:
doit() {
# This solution gives 5-10 MB/s depending on system
# Changed so it now also treats '' as zero
perl -F, -ane 'for(0..$#F) {
# Perl has no beautiful way of matching scientific notation
$s[$_] += $F[$_] !~ /^-?\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?$/m
}
END { $" = ","; print "@s\n" }';
}
export -f doit
doit() {
# Somewhat faster - but regards empty fields as zero
mawk -F"," '{
for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") }
}
END { for(i=1;i<NF;i++) printf cnt[i]","; print cnt[NF] }';
}
export -f doit
要并行化这个,我们需要将大文件拆分成块并将每个块传递给串行解决方案:
# This will spawn a process for each core
parallel --pipe-part -a pht.csv --block -1 doit > blocksums
(您需要使用版本20161222或更高版本才能使用&#39; - 阻止-1&#39;)。
要处理标题,我们计算标题的结果,但我们否定结果:
head -n1 pht.csv | doit | perl -pe 's/(^|,)/$1-/g' > headersum
现在我们可以简单总结一下headerum和blockums:
cat headersum blocksums |
perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] }
END { $" = ",";print "@s\n" }'
或者如果你喜欢逐行输出:
cat headersum blocksums |
perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] }
END { $" = "\n";print "@s\n" }'
答案 2 :(得分:2)
这是你想要做的吗?
$ awk -v RS='[\n,]' '($1+0) != $1' file | sort | uniq -c
1 T1
2 T13
以上使用GNU awk进行多字符RS,并且应该在几秒钟内为您描述的输入文件运行。如果你没有GNU awk,你可以这样做:
$ tr ',' $'\n' < file | awk '($1+0) != $1' | sort | uniq -c
1 T1
2 T13
我正在避免使用,
作为FS的方法,因为那时你必须在循环中使用$i
,这会导致awk为每个输入行进行字段拆分时间,但你可以试试:
$ awk -F, '{for (i=1;i<=NF;i++) if (($i+0) != $i) print $i}' file | sort | uniq -c
1 T1
2 T13
您可以使用非数字值索引的数组在awk中执行唯一计数,但是您可能必须在内存中存储大量数据(与使用临时交换文件的sort
不同所以YMMV采用这种方法。
答案 3 :(得分:0)
我独立解决了它。最后为我做的是以下URL中的动态变量创建示例。 http://cfajohnson.com/shell/cus-faq-2.html#Q24
这是我开发的解决方案。注意:我添加了另一个列,其中包含一些缺失的数据,以便进行更完整的单元测试我不一定是最好的解决方案,也就是TBD。它在我所知道的小csv上正常工作。最好的解决方案还需要在40 GB的csv文件上运行得非常快(未显示哈哈)。
$ cat pht.csv
8,T1,
T13,3,T1
T13,,0
$ tail -n +1 pht.csv | awk -F"," '{ for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") } } END { for(i=1;i<=NF;i++) print cnt[i] }'
2
1
1
PS。老实说,我对自己的答案并不满意。他们说过早优化是万恶之源。那个格言不在这里适用。我真的非常希望gnu并行,如果可能的话,而不是for循环,因为我需要速度。
答案 4 :(得分:0)
最后说明:下面我将分享顺序和并行版本的性能时序,以及最佳可用的单元测试数据集。特别感谢Ole Tange在此应用程序中开发代码以使用他的gnu parallel命令。
单元测试数据文件,最终版本:
$ cat pht2.csv
COLA99,COLB,COLC,COLD
8,T1,,T1
T13,3,T1,0.03
T13,,-6.350818276405334473e-01,-0.036
针对逐列非数字计数的顺序版本对大数据(未显示)进行计时:
ga@ga-HP-Z820:/mnt/fastssd$ time tail -n +2 train_all.csv | awk -F"," '{ for(i=1; i<=NF; i++){ cnt[i]+=(($i+0)!=$i) && ($i!="") } } END { for(i=1;i<=NF;i++) print cnt[i] }' > /dev/null
real 35m37.121s
针对逐列非数字计数的并行版本的大数据计时:
# Correctness - 2 1 1 1 is the correct output.
#
# pht2.csv: 2 1 1 1 :GOOD
# train_all.csv:
# real 1m14.253s
doit1() {
perl -F, -ane 'for(0..$#F) {
# Perl has no beautiful way of matching scientific notation
$s[$_] += $F[$_] !~ /^-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?$/m
}
END { $" = ","; print "@s\n" }';
}
# pht2.csv: 2 1 1 1 :GOOD
# train_all.csv:
# real 1m59.960s
doit2() {
mawk -F"," '{
for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") }
}
END { for(i=1;i<NF;i++) printf cnt[i]","; print cnt[NF] }';
}
export -f doit1
parallel --pipe-part -a "$fn" --block -1 doit1 > blocksums
if [ $csvheader -eq 1 ]
then
head -n1 "$fn" | doit1 | perl -pe 's/(^|,)/$1-/g' > headersum
cat headersum blocksums | perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] } END { $" = "\n";print "@s\n" }' > "$outfile"
else
cat blocksums | perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] } END { $" = "\n";print "@s\n" }' > "$outfile"
fi
新:以下是顺序代码中的ROW方式(非列式):
tail -n +2 train_all.csv | awk -F"," '{ cnt=0; for(i=1; i<=NF; i++){ cnt+=(($i+0)!=$i) && ($i!="") } print cnt; }' > train_all_cnt_nonnumerics_rowwwise.out.txt
背景信息:项目机器学习。这是数据探索的一部分。在使用三星950 Pro SSD存储的双至强32虚拟/ 16物理核心共享内存主机上看到 ~25x并行加速:(32x60)秒连续时间,74秒并行时间。真棒!