Как вычислить частоту букв в строке, используя встроенную карту питонов и сократить функции
Я хотел бы вычислить частоту букв в строке, используя карту питонов, и сократить встроенные функции. Может ли кто-нибудь рассказать о том, как я могу это сделать?
Что у меня так далеко:
s = "the quick brown fox jumped over the lazy dog"
# Map function
m = lambda x: (x,1)
# Reduce
# Add the two frequencies if they are the same
# else.... Not sure how to put both back in the list
# in the case where they are not the same.
r = lambda x,y: (x[0], x[1] + y[1]) if x[0] == y[0] else ????
freq = reduce(r, map(m, s))
Это прекрасно работает, когда все буквы одинаковы.
>>> s
'aaaaaaa'
>>> map(m, s)
[('a', 1), ('a', 1), ('a', 1), ('a', 1), ('a', 1), ('a', 1), ('a', 1)]
>>> reduce(r, map(m, s))
('a', 7)
Как мне заставить его работать хорошо, когда есть разные буквы?
4 ответа
Остановившись на мгновение на вопрос о вашем коде, я укажу, что один из обычных (и самых быстрых) способов подсчета вещей - это класс Counter из модуля коллекций. Вот пример его использования в интерпретаторе Python 2.7.3:
>>> from collections import Counter
>>> lets=Counter('aaaaabadfasdfasdfafsdff')
>>> lets
Counter({'a': 9, 'f': 6, 'd': 4, 's': 3, 'b': 1})
>>> s = "the quick brown fox jumped over the lazy dog"
>>> Counter(s)
Counter({' ': 8, 'e': 4, 'o': 4, 'd': 2, 'h': 2, 'r': 2, 'u': 2, 't': 2, 'a': 1, 'c': 1, 'b': 1, 'g': 1, 'f': 1, 'i': 1, 'k': 1, 'j': 1, 'm': 1, 'l': 1, 'n': 1, 'q': 1, 'p': 1, 'w': 1, 'v': 1, 'y': 1, 'x': 1, 'z': 1})
Чтобы использовать уменьшение, определите вспомогательную функцию addto(oldtotal,newitem)
это добавляет newitem
в oldtotal
и возвращает новый итог. Инициализатором для итога является пустой словарь, {}
, Вот интерпретированный пример. Обратите внимание, что второй параметр get() является значением по умолчанию, которое используется, когда ключ еще не находится в словаре.
>>> def addto(d,x):
... d[x] = d.get(x,0) + 1
... return d
...
>>> reduce (addto, s, {})
{' ': 8, 'a': 1, 'c': 1, 'b': 1, 'e': 4, 'd': 2, 'g': 1, 'f': 1, 'i': 1, 'h': 2, 'k': 1, 'j': 1, 'm': 1, 'l': 1, 'o': 4, 'n': 1, 'q': 1, 'p': 1, 'r': 2, 'u': 2, 't': 2, 'w': 1, 'v': 1, 'y': 1, 'x': 1, 'z': 1}
Код, показанный ниже, печатает время выполнения для 1000 проходов каждого из нескольких методов. При запуске на старой системе AMD Athlon 5000+ Linux 3.2.0-32 Ubuntu 12 с двумя разными строками s
он напечатал:
String length is 44 Pass count is 1000
horsch1 : 0.77517914772
horsch2 : 0.778718948364
jreduce : 0.0403778553009
jcounter: 0.0699260234833
String length is 4931 Pass count is 100
horsch1 : 8.25176692009
horsch2 : 8.14318394661
jreduce : 0.260674953461
jcounter: 0.282369852066
(Метод сокращения работает немного быстрее, чем метод Counter.) Ниже приведен временной код. Он использует модуль timeit. В коде, как здесь, первый параметр timeit.Timer
это код для многократной синхронизации, а вторым параметром является код настройки.
import timeit
from collections import Counter
passes = 1000
m1 = lambda x: [int(ord(x) == i) for i in xrange(65,91)]
def m2(x):
return [int(ord(x) == i) for i in xrange(65,91)]
def es1(s):
add = lambda x,y: [x[i]+y[i] for i in xrange(len(x))]
freq = reduce(add,map(m1, s.upper()))
return freq
def es2(s):
add = lambda x,y: [x[i]+y[i] for i in xrange(len(x))]
freq = reduce(add,map(m2, s.upper()))
return freq
def addto(d,x):
d[x] = d.get(x,0) + 1
return d
def jwc(s):
return Counter(s)
def jwr(s):
return reduce (addto, s, {})
s = "the quick brown fox jumped over the lazy dog"
print 'String length is',len(s), ' Pass count is',passes
print "horsch1 :",timeit.Timer('f(s)', 'from __main__ import s, m1, es1 as f').timeit(passes)
print "horsch2 :",timeit.Timer('f(s)', 'from __main__ import s, m2, es2 as f').timeit(passes)
print "jreduce :",timeit.Timer('f(s)', 'from __main__ import s, addto, jwr as f').timeit(passes)
print "jcounter:",timeit.Timer('f(s)', 'from __main__ import s, Counter,jwc as f').timeit(passes)
Вы также можете использовать метод s.count:
{x: s.count(x) for x in set(s)}
Обратите внимание, что я использовал set(s)
вычислить частоту каждой буквы в строке только один раз. Это результат тестов на моей машине:
String length is 44 Pass count is 1000
horsch1 : 0.317646980286
horsch2 : 0.325616121292
jreduce : 0.0106990337372
jcounter : 0.0142340660095
def_dict : 0.00750803947449
just_dict: 0.00737881660461
s_count : 0.00887513160706
String length is 4400 Pass count is 100
horsch1 : 3.24123382568
horsch2 : 3.23079895973
jreduce : 0.0944828987122
jcounter : 0.102299928665
def_dict : 0.0341360569
just_dict: 0.0643239021301
s_count : 0.0224709510803
Это тестовый код:
import timeit
from collections import Counter, defaultdict
passes = 100
m1 = lambda x: [int(ord(x) == i) for i in xrange(65,91)]
def m2(x):
return [int(ord(x) == i) for i in xrange(65,91)]
def es1(s):
add = lambda x,y: [x[i]+y[i] for i in xrange(len(x))]
freq = reduce(add,map(m1, s.upper()))
return freq
def es2(s):
add = lambda x,y: [x[i]+y[i] for i in xrange(len(x))]
freq = reduce(add,map(m2, s.upper()))
return freq
def addto(d,x):
d[x] = d.get(x,0) + 1
return d
def jwc(s):
return Counter(s)
def jwr(s):
return reduce (addto, s, {})
def def_dict(s):
d = defaultdict(int)
for i in s:
d[i]+=1
return d
def just_dict(s):
freq = {}
for i in s:
freq[i]=freq.get(i, 0) + 1
return freq
def s_count(s):
return {x: s.count(x) for x in set(s)}
s = "the quick brown fox jumped over the lazy dog"*100
print 'String length is',len(s), ' Pass count is',passes
print "horsch1 :",timeit.Timer('f(s)', 'from __main__ import s, m1, es1 as f').timeit(passes)
print "horsch2 :",timeit.Timer('f(s)', 'from __main__ import s, m2, es2 as f').timeit(passes)
print "jreduce :",timeit.Timer('f(s)', 'from __main__ import s, addto, jwr as f').timeit(passes)
print "jcounter :",timeit.Timer('f(s)', 'from __main__ import s, Counter,jwc as f').timeit(passes)
print "def_dict :",timeit.Timer('f(s)', 'from __main__ import s, defaultdict, def_dict as f').timeit(passes)
print "just_dict:",timeit.Timer('f(s)', 'from __main__ import s, just_dict as f').timeit(passes)
print "s_count :",timeit.Timer('f(s)', 'from __main__ import s, s_count as f').timeit(passes)
ord()
обычно дает номер ASCII. Мой метод вычисляет частоту для букв, где каждый индекс соответствует букве, которая является той позицией в алфавите. Поскольку вы вводите строку в верхнем регистре, этот метод не чувствителен к регистру.
s = "the quick brown fox jumped over the lazy dog"
# Map function
m = lambda x: [ord(x) == i for i in xrange(0,26)]
add = lambda x,y: [x[i]+y[i] for i in xrange(len(x))]
freq = reduce(add,map(m, s.upper()))
Вы также можете использовать defaultdict
:
>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> s = 'the quick brown fox jumped over the lazy dog'
>>> for i in s:
... d[i] += 1
...
>>> for letter,count in d.iteritems():
... print letter,count
...
8 # number of spaces
a 1
c 1
b 1
e 4
d 2
g 1
f 1
i 1
h 2
k 1
j 1
m 1
l 1
o 4
n 1
q 1
p 1
r 2
u 2
t 2
w 1
v 1
y 1
x 1
z 1