Вложенные для-циклы и словари при поиске значения в строке

Мне было поручено создать словарь, ключи которого являются элементами, найденными в строке, и значения которых подсчитывают количество вхождений на каждое значение.

Ex.

"abracadabra" → {'r': 2, 'd': 1, 'c': 1, 'b': 2, 'a': 5}

У меня здесь есть логика for-loop:

xs = "hshhsf"
xsUnique = "".join(set(xs))

occurrences = []
freq = []

counter = 0

for i in range(len(xsUnique)):
    for x in range(len(xs)):
        if xsUnique[i] == xs[x]:
            occurrences.append(xs[x])
            counter += 1
    freq.append(counter)
    freq.append(xsUnique[i])
counter = 0 

Это делает именно то, что я хочу сделать, за исключением списков вместо словарей. Как я могу сделать это так counter становится значением, а xsUnique[i] становится ключом в новом словаре?

Ответ 1

Самый простой способ - использовать счетчик:

>>> from collections import Counter
>>> Counter("abracadabra")
Counter({'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1})

Если вы не можете использовать библиотеку Python, вы можете использовать dict.get со значением по умолчанию 0, чтобы сделать свой собственный счетчик:

s="abracadabra"
count={}
for c in s:
    count[c] = count.get(c, 0)+1

>>> count
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}    

Или вы можете использовать dict.fromkeys(), чтобы установить все значения в счетчике равными нулю, а затем использовать это:

>>> counter={}.fromkeys(s, 0)
>>> counter
{'a': 0, 'r': 0, 'b': 0, 'c': 0, 'd': 0}
>>> for c in s:
...    counter[c]+=1
... 
>>> counter
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}

Если вы действительно хотите наименее Pythonic, то есть, что вы можете сделать на C, вы могли бы сделать:

  • создать список для всех возможных значений ascii, установленных на 0
  • цикл над символами строки и количества, которые присутствуют
  • Печать ненулевых значений

Пример:

ascii_counts=[0]*255
s="abracadabra"

for c in s:
    ascii_counts[ord(c)]+=1

for i, e in enumerate(ascii_counts):
    if e:
        print chr(i), e 

Печать

a 5
b 2
c 1
d 1
r 2

Это не масштабируется для использования с Unicode, так как вам понадобится более 1 миллиона записей в списке...

Ответ 2

Вы можете использовать функцию zip для преобразования списка в словарь:

>>> dict(zip(freq[1::2],freq[0::2]))
{'h': 3, 's': 2, 'f': 1}

Но как более пифонический и довольно оптимизированный способ, я предлагаю использовать collections.Counter

>>> from collections import Counter
>>> Counter("hshhsf")
Counter({'h': 3, 's': 2, 'f': 1})

И, как вы сказали, вы не хотите импортировать какой-либо модуль, вы можете использовать словарь, используя метод dict.setdefault и простой цикл:

>>> d={}
>>> for i in xs:
...    d[i]=d.setdefault(i,0)+1
... 
>>> d
{'h': 3, 's': 2, 'f': 1}

Ответ 3

Я предполагаю, что причина a learning в том, почему вы используете два forloops? Во всяком случае, это несколько разных решений:

# Method 1
xs = 'hshhsf'
xsUnique = ''.join(set(xs))

freq1 = {}
for i in range(len(xsUnique)):
    for x in range(len(xs)):
        if xsUnique[i] == xs[x]:
            if xs[x] in freq1:
                freq1[xs[x]] += 1
            else:
                freq1[xs[x]] = 1 # Introduce a new key, value pair

# Method 2
# Or use a defaultdict that auto initialize new values in a dictionary
# https://docs.python.org/2/library/collections.html#collections.defaultdict

from collections import defaultdict

freq2 = defaultdict(int) # new values initialize to 0
for i in range(len(xsUnique)):
    for x in range(len(xs)):
        if xsUnique[i] == xs[x]:
            # no need to check if xs[x] is in the dict because 
            # defaultdict(int) will set any new key to zero, then
            # preforms it operation.
            freq2[xs[x]] += 1


# I don't understand why your using 2 forloops though

# Method 3
string = 'hshhsf' # the variable name `xs` confuses me, sorry

freq3 = defaultdict(int)
for char in string:
    freq3[char] += 1

# Method 4
freq4 = {}
for char in string:
    if char in freq4:
        freq4[char] += 1
    else:
        freq4[char] = 1



print 'freq1: %r\n' % freq1
print 'freq2: %r\n' % freq2
print 'freq3: %r\n' % freq3
print 'freq4: %r\n' % freq4

print '\nDo all the dictionaries equal each other as they stand?'
print 'Answer: %r\n\n'  % (freq1 == freq2 and freq1 == freq3 and freq1 == freq4)

# convert the defaultdict to a dict for consistency
freq2 = dict(freq2)
freq3 = dict(freq3)

print 'freq1: %r' % freq2
print 'freq2: %r' % freq2
print 'freq3: %r' % freq3
print 'freq4: %r' % freq4

Выход

freq1: {'h': 3, 's': 2, 'f': 1}
freq2: defaultdict(<type 'int'>, {'h': 3, 's': 2, 'f': 1})
freq3: defaultdict(<type 'int'>, {'h': 3, 's': 2, 'f': 1})
freq4: {'h': 3, 's': 2, 'f': 1}

Do all the dictionaries equal each other as they stand?
Answer: True


freq1: {'h': 3, 's': 2, 'f': 1}
freq2: {'h': 3, 's': 2, 'f': 1}
freq3: {'h': 3, 's': 2, 'f': 1}
freq4: {'h': 3, 's': 2, 'f': 1}
[Finished in 0.1s]

Или, как указано в dawg, используйте Counter из стандартной библиотеки коллекций

счетчик документов

https://docs.python.org/2/library/collections.html#collections.Counter

defaultdict docs

https://docs.python.org/2/library/collections.html#collections.defaultdict

документы коллекционной библиотеки

https://docs.python.org/2/library/collections.html