我有一个文本文件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解决方案?
答案 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)