计算行出现次数除以总行数 - unix / python

时间:2014-08-06 09:29:08

标签: python unix count text-files uniq

我有一个文本文件test.in

english<tab>walawala
foo bar<tab>laa war
foo bar<tab>laa war
hello world<tab>walo lorl
hello world<tab>walo lorl
foo bar<tab>laa war

所需的输出应为:

english<tab>walawala<tab>0.1666
foo bar<tab>laa war<tab>0.5
hello world<tab>walo lorl<tab>0.3333

新列是行数除以总行数。

目前我正在这样做:

cat test.in | uniq -c | awk '{print $2"\t"$3"\t"$1}' > test.out

但这只给了我计算线而不是概率。此外,我的文件非常庞大,例如1,000,000,000行,每列至少20个字符。

如何正确快速地获得所需的输出?

是否有一个快速的pythonic解决方案?

8 个答案:

答案 0 :(得分:7)

请注意,uniq只计算重复的行,并且必须先排序才能考虑文件中的所有行。对于sort | uniq -c,使用collections.Counter的以下代码更有效,因为它根本不需要对任何内容进行排序:

from collections import Counter

with open('test.in') as inf:
    counts = sorted(Counter(line.strip('\r\n') for line in inf).items())
    total_lines = float(sum(i[1] for i in counts))
    for line, freq in counts:
         print("{}\t{:.4f}".format(line, freq / total_lines))

此脚本输出

english<tab>walawala<tab>0.1667
foo bar<tab>laa war<tab>0.5000
hello world<tab>walo lorl<tab>0.3333

用于说明中给出的输入。


但是,如果您只需要合并连续的行,例如 uniq -c ,请注意使用Counter的任何解决方案都会给出问题中给出的输出,但是{{1}方法uniq -c的输出:

uniq -c will be

<强>不

  1 english<tab>walawala
  2 foo bar<tab>laa war
  2 hello world<tab>walo lorl
  1 foo bar<tab>laa war

如果这是您想要的行为,您可以使用itertools.groupby

  1 english<tab>walawala
  3 foo bar<tab>laa war
  2 hello world<tab>walo lorl

不同之处在于,如果from itertools import groupby with open('foo.txt') as inf: grouper = groupby(line.strip('\r\n') for line in inf) items = [ (k, sum(1 for j in i)) for (k, i) in grouper ] total_lines = float(sum(i[1] for i in items)) for line, freq in items: print("{}\t{:.4f}".format(line, freq / total_lines)) 具有您指定的内容,则uniq管道将生成您在示例中给出的输出,而不是您将得到:

test.in

由于这不是您的输入示例所说的,可能是您无法english<tab>walawala<tab>0.1667 foo bar<tab>laa war<tab>0.3333 hello world<tab>walo lorl<tab>0.3333 foo bar<tab>laa war<tab>0.1667 使用uniq来解决您的问题 - 然后您需要诉诸我的第一个示例Python肯定比你的Unix命令行更快。


顺便说一句,这些在所有蟒蛇中的作用相同&gt; 2.6。

答案 1 :(得分:3)

这是一个纯粹的AWK解决方案:

<test.in awk '{a[$0]++} END {for (i in a) {print i, "\t", a[i]/NR}}'

它使用AWK的数组和特殊变量NR,它跟踪行数。

让我们剖析代码。第一个块

{a[$0]++}

对输入中的每一行执行一次。这里$0表示每一行,它用作数组a的索引,因此只需计算每行的出现次数

第二块

END {for (i in a) {print i, "\t", a[i]/NR}}

在输入的末尾执行。此时,a包含输入中每一行的出现次数,并由行本身索引:因此,通过循环遍历它,我们可以打印一个行表和相对出现(我们除以总行数NR)。

答案 2 :(得分:2)

from collections import Counter

with open('data.txt') as infile:
    # Counter will treat infile as an iterator and exhaust it
    counter = Counter(infile)

    # Don't know if you need sorting but this will sort in descending order
    counts = ((line.strip(), n) for line, n in counter.most_common())

    # Convert to proportional amounts
    total = sum(counter.values())
    probs = [(line, n / total) for line, n in counts]

    print("\n".join("{}{}".format(*p) for p in probs))

这有几个优点。它遍历文件中的行而不是加载整个文件,它利用了现有的Counter功能,它可以排序,并清楚它是什么。

答案 3 :(得分:1)

Python中的解决方案,但我不确定1,000,000,000行的性能。

d = {}
s = "english<tab>walawala\nfoo bar<tab>laa war\nfoo bar<tab>laa war\nhello world<tab>walo lorl\nhello world<tab>walo lorl\nfoo bar<tab>laa war"
c = 0

for l in s.split("\n"):
  c += 1
  if d.has_key(l):
    d[l] += 1
  else:
    d[l] = 1

for k,v in d.items():
  print k + " -> " + str(float(v)/float(c))

输出:

english<tab>walawala -> 0.166666666667
foo bar<tab>laa war -> 0.5
hello world<tab>walo lorl -> 0.333333333333

编辑:使用Python中的Counter对象可以改进此解决方案:https://docs.python.org/2/library/collections.html#collections.Counter

答案 4 :(得分:1)

也许在python中使用自动只能有一个值的字典

from collections import defaultdict

my_dict_counter = defaultdict(float)
counter = 0

for line in open('test.in'):
    my_dict_counter[line] += 1
    counter += 1 

for line in my_dict_counter:
    print line.strip() + "\t" + str(my_dict_counter[line]/counter)

答案 5 :(得分:1)

Python中的另一个解决方案:

my_dict = {}
counter = 0 
with open('test.in') as f:
    for line in f:
        counter += 1
        try:
            my_dict[line] = (my_dict[line]+1)
        except:
            my_dict[line] = 1

for line in my_dict:
    print("%s%s%.4f" % (line[:-1], "<tab>", my_dict[line]/float(counter)))

输出:

english<tab>walawala<tab>0.1667
hello world<tab>walo lorl<tab>0.3333
foo bar<tab>laa war<tab>0.5000

答案 6 :(得分:1)

from collections import Counter
with open("test.in") as f:
    counts = Counter(f)
total = sum(counts.values())
for k, v in counts.items():
    print("{0}<tab>{1:0.4f}".format(k.strip(), v / total))

这不会对概率进行排序。由于三个循环,性能为O(3n),并且可以通过使用TexIOBase的子类来减少到O(2n),该子类跟踪计数器的行或子类,以跟踪处理的总行数。

答案 7 :(得分:1)

如果在RAM中进行所有处理过于昂贵,您可以考虑使用简单的数据库。 sqlite附带python的所有安装。这个例子可以很容易地进行优化,但我觉得简单性在展示方法时更有利于速度:

import sqlite3

conn = sqlite3.connect('counts.db')
c = conn.cursor()

c.execute('CREATE TABLE counts (phrase TEXT PRIMARY KEY, num INT)')
conn.commit()

recs = 0
with open('test.in') as fin:
    for line in fin:
        recs += 1

        # see if already exists
        c.execute("SELECT count(1) FROM counts WHERE phrase=?", (line,))
        count = int(c.fetchone()[0]) + 1
        if count == 1:
            # add new record
            c.execute("INSERT INTO counts VALUES(?,1)", (line,))
        else:
            # update record
            c.execute("UPDATE counts SET num=?", (count,))

        if recs % 10000 == 0:
            conn.commit()

conn.commit()

for row in c.execute("SELECT phrase,num FROM counts ORDER BY phrase"):
    print "%s\t%f" % (row[0], float(row[1]) / recs)