2015-02-15 4 views
3

Допустим, у меня есть длинный список этого типа:нахождение частот парных элементов в списке пар

text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b'], ... ] 

Учитывая первые элементы, я хочу, чтобы построить словарь, который будет показывать счетчик второй элементы. Например, в конкретном примере выше, я хотел бы иметь что-то вроде этого:

{'a': {'b':2, 'd':1}, 
'w': {'a':1} 
} 

Вот как я безуспешно пытался решить. Я построил список уникальных первых элементов. Давайте назовем это words, а затем:

dic = {} 

for word in words: 
    inner_dic = {} 
    for pair in text: 
    if pair[0] == word: 
     num = text.count(pair) 
     inner_dic[pair[1]] = num 
    dic[pair[0]] = inner_dic 

я получаю явно ошибочный результат. Одна из проблем с кодом заключается в том, что он переопределяет пары. Я не уверен, как это решить.

+1

Использование .count неэффективно –

ответ

4

Вы должны сделать это вместо того, чтобы:

for word in words: 
    inner_dic = {} 
    for pair in text: 
    if pair[0] == word: 
     num = text.count(pair) 
     inner_dic[pair[1]] = num 
    dic[word] = inner_dic 

то есть, вы должны делать dic[word], а не dic[pair[0]], который назначит inner_dic к первому элементу в последнем pair проверено, даже если pair[0] не word.

+2

Спасибо. Это лучший ответ, потому что вы действительно читали мой код и видели, где он поступил неправильно, вместо того, чтобы писать что-то с нуля, используя готовые библиотеки. Престижность! –

+0

Я считаю, что есть отдельный форум для просто кода-обзора. В то же время должно быть какое-то значение для ответов, которые демонстрируют большинство решений Pythonic, чтобы другие могли извлечь выгоду. В противном случае у нас остался код, который неэффективен (например, * str.count *), который использует индексирование вместо распаковки (например, '' pair [0] '' и pair [1] '' versus '' для первого , второй в тексте ''), и это позволяет избежать стандартных библиотечных решений, специально предназначенных для решения именно такого рода проблем). Поэтому, хотя вы можете быть счастливы, что кто-то заметил ошибку в вашем коде, ответ, который вы выбрали, не так уж хорош. –

+0

@ RaymondHettinger: Спасибо за ваш вклад. Я согласен с тем, что, если учитывать только эффективность, есть лучшие ответы. Но никто изначально не указал на неэффективность моего небольшого фрагмента кода. Должна быть также некоторая ценность для ответов, которые учитывают попытки и ограничения OP. В противном случае это станет соревнованием, в котором кто-то поставит самый питоновский код раньше, не обращая внимания на процесс мышления человека, который пытается решить свою проблему. Во всяком случае, спасибо за ваш отличный ответ, и я приношу свои извинения, если вы думаете, что я не делал правосудия. –

5

Вы можете использовать defaultdict в сочетании с Counter Dict:

from collections import Counter, defaultdict 
d = defaultdict(Counter) 

text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b'] ] 

for k, v in text: 
    d[k][v] += 1 # for single value 
    # d[k].update(v) for multiple values i.e list of words 

from pprint import pprint as pp 

pp(d) 
{'a': Counter({'b': 2, 'd': 1}), 
'w': Counter({'a': 1})} 

defaultdict создаст новый спаривание ключ/значение, где значение является счетчиком ДИКТ, если ключ не существует, если ключ существует мы просто обновляем значение с помощью параметра Counter.update, который, в отличие от dict.update, будет увеличивать значение, не переписываемое.

используя обычный dict без импорта мы можем использовать dict.setdefault, который будет создавать новую Dict в качестве значения для каждого ключа k и установить значение по умолчанию 0 для каждого подраздела v:

d = {} 
text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b'] ] 

for k, v in text: 
    d.setdefault(k, {}).setdefault(v,0) 
    d[k][v] += 1 
pp(d) 
{'a': {'b': 2, 'd': 1}, 'w': {'a': 1}} 
+0

Я хочу сохранить окончательный словарь, используя 'json'. Извините, если вопрос слишком наивен, но делает ли этот лишний метод «Счетчик» в конечном текстовом файле, если я сбрасываю его как есть? –

+0

@schmutter, вы можете приложить к нормальному dict в конце, если потребуется, есть ли какая-то конкретная причина, по которой вы хотите? –

+0

Спасибо. Хороший ответ. –

6

collections module делает коротким работа таких задач.

Используйте счетчик Counter (это словарь, который возвращает 0 для отсутствующих значений, что упрощает использование +=1 для увеличения количества очков). Используйте defaultdict для внешнего Dict (она может автоматически создать новый счетчик для каждого «первого» префикса):

>>> from collections import defaultdict, Counter 
>>> d = defaultdict(Counter) 
>>> text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b']] 
>>> for first, second in text: 
    d[first][second] += 1 

Вот эквивалент, используя обычные словари:

text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b']] 

d = {} 
for first, second in text: 
    if first not in d: 
     d[first] = {} 
    inner_dict = d[first] 
    if second not in inner_dict: 
     inner_dict[second] = 0 
    inner_dict[second] += 1 

Либо короткий путь или длинный путь будет отлично работать с json module (оба Счетчик и defaultdict - это виды диктов, которые могут быть закодированы JSON).

Надеюсь, это поможет. Удачи вам в вашем анализе текста :-)

0
text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b']] 
d = {} 
for i in text: 
    if d.get(i[0]): 
     if d[i[0]].get(i[1]): 
      d[i[0]][i[1]] +=1 
     else: 
      d[i[0]][i[1]] = 1 
    else: 
     d[i[0]] = {i[1] : 1} 
print d 
>>>{'a': {'b': 2, 'd': 1}, 'w': {'a': 1}} 
+0

Хорошей новостью является то, что это дает правильный ответ. Немного плохая новость заключается в том, что использование * dict.get * вместо * in * -оператора медленнее, менее понятно и немного рискованно (для приложений, где * None * является допустимым значением в dict). В основном плохая новость заключается в том, что этот код далек от Pythonic - повторное использование индексации, '' i [0] '' и '' i [1] '' делает код почти нечитаемым. –

1

Вот способ, используя метод .setdefault:

text = [ ['a', 'b'], ['a', 'd'], ['w', 'a'], ['a', 'b'] ] 
result={} 
for x, y in text: 
    result.setdefault(x, {}).setdefault(y,0) 
    result[x][y]+=1 

>>> result 
{'a': {'b': 2, 'd': 1}, 'w': {'a': 1}} 

Нет внешних библиотек требуется.

+0

+1 для прямого использования * dict.setdefault *, который был разработан для решения именно этой проблемы. Слишком плохо, что OP не любит использовать стандартную библиотеку, которая предлагает даже более чистые решения. –

Смежные вопросы