【问题标题】:Letter Frequency: get frequencies to print in order from most to least used字母频率:按使用最多到最少的顺序获取要打印的频率
【发布时间】:2012-04-14 03:16:06
【问题描述】:

我想编写一个程序,从文本文件中读取字母,忽略符号和空格,并按从最常见到最不常见的顺序打印每个字母的计数。

另外,我正在为我的第一个编程课做这个,所以我不允许使用计数器。

到目前为止我有这个:

name= raw_input("Enter file name:")
fl= open(name, 'r+').read()
lw= fl.lower()
ws= lw.replace(' ','')
sm= ws.translate(None, ",-!.;?:")
occ= {}
alpha= list ('abcdefghijklmnopqrstuvwxyz')
for x in alpha:
    occ[x]= sm.count(x)
for x in occ:
    print x, occ[x]

假设文本文件是这样的:“我对此非常困惑?”

然后程序将通过它的编程并执行此操作:“iamhighlyconfusedbythis”

但现在它只显示如下内容:

1   a
1   b
1   c
1   d
1   e
1   f
1   g
3   h
3   i
0   j
0   k
1   l
1   m
1   n
1   o
0   p
0   q
0   r
2   s
1   t
1   u
0   v
0   w
0   x
2   y
0   z

但我希望结果看起来像:

3   h
3   i
2   s
2   y
1   e
1   f
1   g
1   a
1   b
1   l
1   m
1   n
1   o
1   c
1   t
1   u
1   d
0   j
0   k
0   p
0   q
0   r
0   v
0   w
0   x
0   z

我使用的想法来自:

间谍

Determining Letter Frequency Of Cipher Text

SimplyZ

Letter frequency in python

【问题讨论】:

  • 塞缪尔,请清理一下。很难按原样阅读。
  • 抱歉 Michael Petrotta 和 jamylak 我不小心拿错了程序,我只是编辑了它以放入正确的程序......这是我到目前为止得到的那个......再次抱歉......
  • @JoelCornett- 在某种程度上是的,在某种程度上不是......这不是我真正想理解的书中的分级练习......
  • 您实际上不必制作一个 alpha 列表:list('abc...xyz') 如果您只是在 for 循环中使用该字符串,Python 将自动循环该字符串,一次一个字符.

标签: python


【解决方案1】:

更新问题的解决方案

>>> from collections import defaultdict
>>> import string
>>> text = 'I am highly confused by this?'.lower().translate(None,string.punctuation+' ')
>>> c = defaultdict(int)
>>> c.update({letter:0 for letter in string.lowercase[:26]}) #Initialize each letter of alphabet to 0
>>> for letter in text:
        c[letter] += 1


>>> for letter,freq in sorted(c.iteritems(),key=lambda (l,f): (-f,l)): #Sort by frequency in descending order by making frequency negative then by letter in ascending order
        print freq, letter


3 h
3 i
2 s
2 y
1 a
1 b
1 c
1 d
1 e
1 f
1 g
1 l
1 m
1 n
1 o
1 t
1 u
0 j
0 k
0 p
0 q
0 r
0 v
0 w
0 x
0 z

原始问题的解决方案: 你可以使用collections.Counter:

>>> from collections import Counter
>>> import string
>>> text = 'I am highly confused by this?'.translate(None,string.punctuation+' ')
>>> print ' '.join('%d %s'%(freq,letter) for letter,freq in Counter(x).most_common())
3 h 2 i 2 s 2 y 1 a 1 c 1 b 1 e 1 d 1 g 1 f 1 I 1 m 1 l 1 o 1 n 1 u 1 t

【讨论】:

  • 谢谢您的回复和解答,但是我不允许使用Counter,有没有别的办法?
  • 你在问题​​中提到过吗?看的时候没看到,反正改了这个问题我会再做一个新的解决方案。
  • 你可以使用defaultdict吗?
  • 我又一次做出了愚蠢的新手动作……我再次对其进行了编辑,以包含我所知道的所有内容。感谢您忍受 jamlak 和其他阅读本文的人!!! :)
  • @ jamylak - 我什至不知道 defaultdict 是什么?
【解决方案2】:

你可以这样做,

from operator import itemgetter
for k,v in sorted(occ.items(), key=itemgetter(1), reverse=True):
    print k, v

但是有更好的方法来计算字母,例如collections.Counter

【讨论】:

  • 感谢您的快速响应,但是我使用的python程序是Python 2.7,它不知道Counter是什么......另外,当我接受您的编程并尝试合并时与我的它导致一个错误: Traceback(最近一次调用最后一次):文件“C:\ Python27 \ hell \ LtoFreq.py”,第11行,在 for k,v in sorted(occ.items,key = itemgetter(1), reverse=True): NameError: name 'occ' is not defined
  • 应该是items() ?此外,它实际上最终不会按正序对辅助键(字母)进行排序
  • @SamuelM。 - 计数器是在 Python 2.7 中添加的,所以它应该存在。不过,您需要从 collections 导入它。
  • @Brendan Long-感谢您的回复,并容忍了一个编程新手,但我不允许使用计数器(用于课程),还有其他方法吗?
  • @SamuelM。 - gnibbler 的回应应该适合你。使用sorted 函数。他的回答只是一个 sn-p,但您需要更改代码才能使用它。
【解决方案3】:

由于你不能使用 Counter,我会这样做:

from string import ascii_lowercase

with open(name, 'r') as f:
    raw_text = f.read().lower()

letterCounts = [raw_text.count(letter) for letter in ascii_lowercase]

frequencies = reversed(sorted(zip(ascii_lowercase, letterCounts), lambda x: x[1]))

for i in frequencies:
    print "%s: %d" % i

【讨论】:

  • 这会起作用,但如果输入文件非常大,这会很慢。它将整个文件读入内存,然后制作整个文件的另一个副本,副本为小写。然后它将对整个文件进行 26 次单独的传递,计算字母出现次数。我喜欢使用ascii_lowercase;这是一个值得记住的好消息!
  • 是的,我认为这是 O(N^2)。我也刚刚发现了ascii_lowercase...我将使用string.lowercase[:26]的那个座位
  • 公平地说,这几乎就是问题所显示的内容,只是清理和改进了!
  • 是的,我想说你应该将你的评论复制/粘贴到 OP,这样他也能看到。
【解决方案4】:

这是一个使用 defaultdict() 的简单 Python 版本。既然你说这是一个类,这不是你要求的最终解决方案。

from collections import defaultdict
from operator import itemgetter

d = defaultdict(int)

name = raw_input("Enter file name: ")

with open(name, "r") as f:
    for line in f:
        for ch in line:
            if ch.isalpha():
                d[ch] += 1

lst = d.items()

# sort twice: once for alphabetical order, then for frequency (descending).
# Because the Python sort is "stable", we will end up with descending
# frequency, but alphabetical order for any frequency values that are equal.
lst.sort(key=itemgetter(0))
lst.sort(key=itemgetter(1), reverse=True)

for key, value in lst:
    print value, key,

当您对打开的文件执行 for 循环时,例如此处的 for line in f,Python 将一次从输入文件中抓取一行。然后我们在输入行上一次循环一个字符。然后我们检查它是否是一个字母,如果是,我们增加该字母的频率计数。

此代码中存在错误。您希望它计算所有字母的频率,就好像它们都是小写一样,但是此代码将保留一个大写计数和另一个小写计数。我相信您可以弄清楚如何修改它,以便计数仅用于小写。

计数完成后,我们使用.items() 方法函数来获取(key, value) 元组的列表。例如:('h', 3) 是一个带有字典键字符 h 及其值的元组,计数为 3。

现在我们要排序。我正在向您展示 Python 可以做到的一个绝妙技巧:由于排序是“稳定”排序,如果我们进行多个排序,除非必须,否则 Python 不会干扰早期排序的结果。这意味着如果我们首先按字母顺序排序,然后然后按频率计数,那么对于频率计数相等的所有情况,我们将在该频率内获得字母顺序。因此,由于 abc 的频率都相同(它们都出现一次),因此您会期望部分输出为:1 a 1 b 1 c

现在,我有点棘手,但这很好学。 sort 函数可以接受一个名为key 的参数来控制排序。 key 应该是一个返回用于排序的值的函数。由于我们有一个元组列表,我们需要一个可以获取元组的一部分并返回它的键函数。我们可以写两个函数:

def get_key(kv_tuple):
    return kv_tuple[0]
def get_value(kv_tuple):
    return kv_tuple[1]

但是 Python 有一个函数,operator.itemgetter(),我们可以使用它。如果我们告诉它我们想要获取元组中的哪个位置,它将为我们创建一个关键函数,它将为我们获取元组的一部分。

由于我们希望频率首先按最大值排序,因此我们还在 .sort() 方法的参数中设置了 reverse=True

最后我们遍历键、值元组的列表并打印。

这段代码还有另一个问题。您的示例输出显示您希望每个字母都在列表中,如果字母不在输入中,则计数为 0。这只计算那里的东西。

所以,我建议你重新编写这段代码。不要使用defaultdict,而是尝试使用普通的dict,但是有一个循环将az 的每个字母设置为dict,计数为0。

我还建议,在您拥有包含您要计数的字母的普通dict 之后,您更改决定是否计数的代码。目前它使用.isalpha()方法函数来决定是否统计一个字符;相反,您可以检查该字符是否为in 字典。然后,您可以使用此代码来计算标点符号或数字或任何类型的字符。

【讨论】:

    【解决方案5】:

    我认为更简单的方法是使用本机计数功能:

    此代码仅打印每个字母的计数。您需要将它放在一个集合中并对其进行排序,以便首先获得最常用的字母。

    text = 'Your original text.'
    alpha = list ('abcdefghijklmnopqrstuvwxyz')
    for letter in alpha:
        print letter + ': ' + str(text.count(letter))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-30
      • 2020-10-12
      • 2020-02-06
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多