【问题标题】:Multiply and sum values in a file将文件中的值相乘和求和
【发布时间】:2015-04-17 08:28:58
【问题描述】:

我们有一个文件 - input.txt - 5 行 9 列的矩阵(实际文件是 10K+ 行和 40K+ 列):

Col1    Col2    Col3    Col4    Col5    Col6    Col7    Col8    Col9
0.8 1   0.8 0.6 0.9 0.4 0.3 0.1 0.6
1   0.6 0.5 0.6 0.3 0.1 0.2 0.5 0.2
0.4 0.5 0.1 0.7 0.8 0.8 0.6 0.3 0.3
0.9 0.2 1   0.1 0.9 0.8 0.6 0.9 0.2
0.9 1   0.2 0.5 0.5 0.7 0.5 0.3 0.2

注意 1: 文件没有标题 - 将其保留在此处以供参考。
注意 2: 解决方案必须扩展到具有 40K+ 列的真实数据。注意 3: 添加了 python 和 perl 标签,以性能更好者为准。

需要将其转换为以下output.txt - 矩阵5rows 3cols:

Col1    Col2    Col3
2.6 1.7 1.3
1.6 0.5 0.9
0.7 2.4 0.9
2.2 2.5 1.3
1.4 1.9 0.7

逻辑:

Output_Col1 = (Input_Col2) + (Input_Col3*2)
Output_Col2 = (Input_Col5) + (Input_Col6*2)
Output_Col3 = (Input_Col8) + (Input_Col9*2)

努力,尝试将第二列和第三列的矩阵文件作为单独的文件,如果我可以将第二个文件乘以 2 然后将这两个文件相加...可能有更简单的方法。

ncol=9
cut -d" " -f`seq -s "," 2 3 $ncol` input.txt > col2s.txt
cut -d" " -f`seq -s "," 3 3 $ncol` input.txt > col3s.txt

【问题讨论】:

  • 如果您的输入文件有 >40,000 列,那么您将遇到的最基本问题是您使用的工具是否可以读取那么大的一行(即一行可能是 160Kb 左右) )较旧的工具可能无法(有些限制为每行 4k)我建议您在应用乘法之前先将它们分解一下。
  • 您需要多久处理一次这样的文件?如果您经常这样做,请记住拆分列将是最重要的瓶颈,您可以通过只关注您需要的列来节省时间,但这需要一些自定义编程:请参阅我的博客文章 Splitting long lines is slow。跨度>
  • 我有大约 900 个文件,压缩后的大小从 500MB 到 3GB 不等。
  • 这听起来与我遇到的问题相似。您肯定希望将文件压缩,并使用类似于我编写的例程来仅提取您想要的字段。新列是要添加到现有列中,还是替换特定列?

标签: python bash perl shell awk


【解决方案1】:

Awk

awk '{print ($2+($3*2)),($5+($6*2)),($8+($9*2))}'

在第一条记录上,打印前三个字段。 然后从字面上打印出您要求的方程式

可扩展版本

awk '{for(i=2;i<=NF;i+=3)x=(x?x FS:"")($i+($(i+1)*2));print x;x=y}' file

输出:

2.6 1.7 1.3
1.6 0.5 0.9
0.7 2.4 0.9
2.2 2.5 1.3
1.4 1.9 0.7

【讨论】:

  • 如何扩展到 40K+ 列?
  • @zx8754 添加了可扩展版本
  • 你可以跳过 NR==1 处理,正如 zx8754 写的Note: files do not have headers - kept it here to reference.
  • @WalterA Cheers 没看到!
  • @zx8754 Gawk 没有限制。
【解决方案2】:

Perl 的救援:

perl -lane 'print join "\t", $F[1] + $F[2] * 2, $F[4] + $F[5] * 2, $F[7] + $F[8] * 2' input.txt > output.txt

解释:

  • -l 在每个 print 之后添加换行符
  • -a 将每一行拆分为 @F 数组
  • -n 逐行读取输入并运行每个代码

如果您有更多相邻的列要处理,您可以使用较短的符号:

print join "\t", map $F[$_] + $F[$_ + 1] * 2, 1, 4, 7

(将1, 4, 7 替换为左列的实际列表)。

【讨论】:

  • 40K 列文件如何扩展?
  • @zx8754: 奶 - 试试看。
  • 谢谢,对不起,我对perl不熟悉,你能解释一下代码吗?
  • @zx8754: print, join, map.
  • @zx8754:列从 0 开始编号。
【解决方案3】:

鉴于您的文件的大小,我的偏好是使用 Python、Perl 或什至 C/C++ 等编译语言中的特定程序来处理它。这可能比 shell 脚本快得多,并且具有更好的错误处理能力。

使用 shell 你可能会使用类似的东西:

# Outer loop deal with each line in the file.
cat my_file | while read line
do
    # Inner loop. Deal with each calculation on the line.
    while [[ ${line} ]]
    do
        echo ${line} | cut -d' ' -f1-3  | nawk '{printf("%d\t",$2+($3*2))}'
        line=$(echo ${line} | cut -s -d' ' -f4-)
    done
    printf "\n"
done

【讨论】:

  • Awk 比 perl 和 python 都快。read 可能是您可以使用的最慢的方法之一。
  • @JID 完全同意 read 的缓慢性 - 我的解决方案表明这是可能的,而不是明智的。本来更喜欢 awk 唯一的解决方案,但我不确定 40K 列的可扩展性。个人可能会为此使用 Python 或 C++。
  • 应该补充一点,我在awk 的排长队经历并不好。多年(实际上是几十年前)我不得不做类似的事情,而 awk 让我非常失望,因为没有记录的 4K 行限制。我不得不争先恐后地找到另一个解决方案,从那时起我一直对将它用于长线应用程序持谨慎态度,尽管我接受nawkgawk 可能不会以这种方式受到限制。所以,归根结底,主要是个人的事情。
  • 很公平,但你是对的,Gawk 没有限制,尽管我不确定其他人。
【解决方案4】:

使用 bc 和循环:

(编辑:摆脱 UUOC)

while read c1 c2 c3 c4 c5 c6 c7 c8 c9 c_others; do
        out1=$(echo $c2 + $c3 \* 2 | bc)
        out2=$(echo $c5 + $c6 \* 2 | bc)
        out3=$(echo $c8 + $c9 \* 2 | bc)
        echo ${out1} ${out2} ${out3}
done < input.txt

结果:

2.6 1.7 1.3
1.6 .5 .9
.7 2.4 .9
2.2 2.5 1.3
1.4 1.9 .7

当你希望浮点数

| sed -e 's/ \./ 0./g' -e 's/^\./0./'

恐怕额外的 sed 解析会使这成为最慢的解决方案。

【讨论】:

  • 对于 4 亿条记录文件来说,这将非常缓慢。另外 OP 说有 40k+ 列。
  • @JID 我写的确实很慢,添加它是为了展示不同的技术(当 OP 不想使用 perl/awk 时)。 10K+ 行应该不是问题。我不知道使用cut -f1-10 进行预处理是否会对您或我有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
  • 2017-11-29
相关资源
最近更新 更多