如何在文本文件中生成数字的连续累积总数?


9

我有一个200万行的文本文件。每行都有一个正整数。我正在尝试形成频率表之类的东西。

输入文件:

3
4
5
8

输出应为:

3
7
12
20

我该怎么做呢?


1
您在文字中说,您想要一个频率。您的输出样本是一个列表。你能澄清一下吗?
Wayne_Yux

确实,您的输出不是频率表
don.joey

对不起。我的意思是累积频率表。修改了问题。谢谢。

这不是很酷,但我通常只是将类似的内容添加到电子表格中。
John U

我通常会使用@JohnU,但是我拥有的文件有100万个数字。

Answers:


20

awk

awk '{total += $0; $0 = total}1'

$0是当前行。因此,对于每行,我将其添加到中total,将行设置为new total,然后尾部1是awk快捷方式-它为每个真实条件打印当前行,并且1条件的计算结果为true。


请您能解释一下您的代码吗?
George Udosen '17

这个词print也可以使用吗?
乔治·乌德森

是的,print total}而不是$0 = total}1
muru

1
@乔治啊,不。
muru

9
编写awk脚本的一种更简短,也许更容易理解的方法是{print(total += $0)}
Miles

9

在python脚本中:

#!/usr/bin/env python3
import sys

f = sys.argv[1]; out = sys.argv[2]

n = 0

with open(out, "wt") as wr:
    with open(f) as read:
        for l in read:
            n = n + int(l); wr.write(str(n)+"\n")

使用

  • 将脚本复制到一个空文件中,另存为 add_last.py
  • 使用源文件和目标输出文件作为参数运行它:

    python3 /path/to/add_last.py <input_file> <output_file>
    

说明

该代码可读性强,但详细说明:

  • 打开输出文件以写入结果

    with open(out, "wt") as wr:
    
  • 打开输入文件以按行读取

    with open(f) as read:
        for l in read:
    
  • 阅读各行,将新行的值添加到总计中:

    n = n + int(l)
    
  • 将结果写入输出文件:

    wr.write(str(n)+"\n")
    


3
这与短促或时间性能无关(百万行不是大数据)。您答案中的代码不是惯用的Python。我的答案只是您的pythonic版本。
jfs

8
@JFSebastian如果惯用的版本比较慢,为什么有人会喜欢它呢?“ pythonic”没有什么特别的,它只是一个约定,可以帮助python开发人员共享代码和可读性标准。如果比较惯用的版本效率较低(较慢),则除非您在标准化比性能更重要的环境中工作(这对我来说听起来像个可怕的主意),否则不应使用它。
terdon '17

2
@terdon关于过早的优化有话要说。由于长期的可维护性,可读性很重要。
muru

4
@muru当然,但这是完全可读的。唯一的犯罪不是“蟒蛇般的”。更不用说我们在谈论的是7行代码,而不是一些大型项目。以样式约定的名义牺牲效率似乎是错误的方法。
terdon

9

纯娱乐

$ sed 'a+p' file | dc -e0 -
3
7
12
20

这是通过一个 ppending +p到输入的各行,然后传递结果给dc计算器,其中

   +      Pops two values off the stack, adds them, and pushes the result.
          The precision of the result is determined only by the values  of
          the arguments, and is enough to be exact.

然后

   p      Prints  the  value on the top of the stack, without altering the
          stack.  A newline is printed after the value.

-e0参数将压0dc堆栈以初始化总和。


像这样的事情实际上可能是大型数据集上最快的
Digital Trauma

@DigitalTrauma上有130万行,实际上几乎是最慢的:real 0m4.234s
Jacob Vlijm

有趣的是,
投票

请稍微解释一下。
AmanicA

8

在Bash中:

#! /bin/bash

file="YOUR_FILE.txt"

TOTAL=0
while IFS= read -r line
do
    TOTAL=$(( TOTAL + line ))
    echo $TOTAL
done <"$file"

bash在这方面极其缓慢:real 0m53.116s几乎一分钟,在130万行上:)
Jacob Vlijm '17

@JacobVlijm破折号的速度大约是busybox ash和zsh(在sh模式下)的1.5倍,但当然,即使破折号也比python慢​​5倍。
muru

6

要在标准输入上每行打印部分给定的整数和:

#!/usr/bin/env python3
import sys

partial_sum = 0
for n in map(int, sys.stdin):
    partial_sum += n
    print(partial_sum)

可运行的示例

如果由于某种原因该命令太慢;您可以使用C程序:

#include <stdint.h>
#include <ctype.h>
#include <stdio.h>

int main(void)
{
  uintmax_t cumsum = 0, n = 0;
  for (int c = EOF; (c = getchar()) != EOF; ) {
    if (isdigit(c))
      n = n * 10 + (c - '0');
    else if (n) { // complete number
      cumsum += n;
      printf("%ju\n", cumsum);
      n = 0;
    }
  }
  if (n)
    printf("%ju\n", cumsum + n);
  return feof(stdin) ? 0 : 1;
}

要构建并运行,请键入:

$ cc cumsum.c -o cumsum
$ ./cumsum < input > output

可运行的示例

UINTMAX_MAX18446744073709551615

对于以下情况生成的输入文件,C代码比我的机器上的awk命令快几倍:

#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')

2
值得一提的是accumulate()itertool
David Z,

5

您可能想要这样的东西:

sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'

命令说明:

  • sort -n <filename> | uniq -c 对输入进行排序并返回频率表
  • | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' 将输出变成更好的格式

示例:
输入文件list.txt

4
5
3
4
4
2
3
4
5

命令:

$ sort -n list.txt | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Number  Frequency
2   1
3   2
4   4
5   2

我喜欢这样,输出很好:)...
George Udosen

5

您可以在vim中执行此操作。打开文件并键入以下按键:

qaqqayiwj@"<C-a>@aq@a:wq<cr>

注意,<C-a>实际上是ctrl-a,<cr>回车符,即enter按钮。

这是这样的。首先,我们要清除寄存器“ a”,以使其在第一次使用时没有副作用。这很简单qaq。然后,我们执行以下操作:

qa                  " Start recording keystrokes into register 'a'
  yiw               " Yank this current number
     j              " Move down one line. This will break the loop on the last line
      @"            " Run the number we yanked as if it was typed, and then
        <C-a>       " increment the number under the cursor *n* times
             @a     " Call macro 'a'. While recording this will do nothing
               q    " Stop recording
                @a  " Call macro 'a', which will call itself creating a loop

该递归宏运行完毕后,我们只需调用:wq<cr>保存并退出即可。


1
+1用于分解魔法咒语并解释所有部分。这些部分太少见了。
John U

5

Perl一线:

$ perl -lne 'print $sum+=$_' input.txt                                                                
3
7
12
20

拥有250万行数字,处理大约需要6.6秒:

$ time perl -lne 'print $sum+=$_' large_input.txt > output.txt                                        
    0m06.64s real     0m05.42s user     0m00.09s system

$ wc -l large_input.txt
2500000 large_input.txt

real 0m0.908s, 相当不错。
雅各布•弗利姆'17

@JacobVlijm是一个非常小的文件。我添加了一个带有250万行文件的小型测试。6.64秒
Sergiy Kolodyazhnyy

1
我跑了130万线的古老制度
雅各布Vlijm

3

一个简单的Bash单行代码:

x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE

x是当前行及以上的所有数字的总和。
n是当前行中的数字。

我们遍历的所有行nINPUT_FILE并将它们的数值添加到变量中,x并在每次迭代期间打印该总和。

虽然Bash在这里有点慢,但是对于具有200万个条目的文件,您可以期望它运行20-30秒左右,而无需将输出打印到控制台(与您使用的方法无关,它甚至更慢)。


3

与@steeldriver的答案类似,但是奥术要少一些bc

sed 's/.*/a+=&;a/' input | bc

bc(and dc)的好处是它们是任意精度的计算器,因此永远不会溢出或遭受整数精度的困扰。

sed表达式将输入转换为:

a+=3;a
a+=4;a
a+=5;a
a+=8;a

然后由评估bc。的aBC变量是自动初始化为0。每个线的增量a,然后明确地打印它。


real 0m5.642s130万行。sed在这方面真的很慢。
雅各布·弗利姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.