问题描述
问题
通常,我需要处理多个CSV文件的目录并生成一个输出文件。通常,我依靠并行的GNU来并行运行这些任务。但是,我需要一种方法来丢弃除返回输出的第一个作业之外的所有作业的第一行(标题)。
x,y
1,1.2
2,5.3
3,6.0
然后,有一些(Python)脚本,称为calc.py
,用于清除数据或进行计算...
import csv
import math
import sys
rdr = csv.DictReader(sys.stdin)
wtr = csv.DictWriter(sys.stdout,fieldnames=['x','y','siny'])
wtr.writeheader()
for row in rdr:
row['siny'] = math.sin(float(row['y']))
wtr.writerow(row)
然后我们可以与GNU并行并行处理数据文件...
parallel --lb python calc.py '<' {} ::: $(ls -1 *.csv)
但是,这将产生多个标题行。例如...
x,y,siny
1,1.2,0.9320390859672263
2,5.3,-0.8322674422239013
3,6.0,-0.27941549819892586
x,siny
4,7.2,0.7936678638491531
5,2.2,0.8084964038195901
6,0.9,0.7833269096274833
我正在寻找一种简单的方法(最好是parallel
的选项),以仅将第一行标题行保留在 output 中。手册中与标题相关的内容似乎与输入有关。
可能的解决方案
我看到了一些选择,但是我什么都不喜欢...
- 没有
calc.py
输出标头,而是在并行运行前回显标头。缺点是必须知道标题,否则我们需要在运行python calc.py data1.csv | head -n 1
之前通过运行parallel
之类的内容来窥视标题。 - 将每个作业的输出保存到单独的文件中,然后事后将它们连接起来(例如,用
xsv
,tail
,sed
等),从除首先。这样做的缺点是必须管理磁盘上的其他文件,然后再清理它们。 - 编写另一个执行此操作的程序,并将
parallel
的结果传递给该程序。
解决此问题的最佳方法是什么? 是否有一个选项可以告诉parallel忽略每个作业输出中除一个标题行外的所有标题行?
解决方法
如何调整选项1:
使程序采用两个参数:文件作业号
if jobnumber == 1:
output header
要确保首先打印作业1,请使用--keep-order
:
parallel --keep-order python calc.py {#} '<' {} ::: *.csv
GNU Parallel会将正在运行的作业的输出缓存在/ tmp中以序列化输出,该输出可能比--lb
慢或不慢。
通常,您可以执行以下操作:
parallel -k python 'calc.py < {} {= uq; $_ = seq()==1 ? "" : "| tail +2" =}' ::: *.csv
uq
自20190722开始可用。
您将运行tail
,因此它可能会稍微慢一些。在我的系统上,tail
每个核心可提供0.5 GB / s的速度。
AFAIK,GNU Parallel没有提供任何这样的选项以仅在输出中写入标头一次
但是一种简单的方法可以是使用awk
只输出标题行一次,就像
parallel --lb python calc.py '<' {} ::: $(ls -1 *.csv) | awk 'NR==1 { header=$0; print $0; next; } $0 != header'
说明
NR
是awk中的行号。当行号为1时,它将存储在变量header
中,并且仅打印一次。如果之后有任何行与标头完全匹配,它将被忽略
专业人士
- 您不需要事先知道标题
- 您不需要将输出保存到单独的文件中
- 无需更改代码
缺点
- 假设没有数据行与标题完全匹配
- 如果没有标题,它将修剪输出的第一行和所有相同的行
- 字符串匹配的开销很小