2017-02-06 1 views
2

Я разбор по некоторым данным и сделали их в словарь следующим образом:граф и удалить дубликаты в ключах при сохранении значения

gen_dict = { 
"item_C_v001" : "jack", 
"item_C_v002" : "kris", 
"item_A_v003" : "john", 
"item_B_v006" : "peter", 
"item_A_v005" : "john", 
"item_A_v004" : "dave" 
} 

Я пытаюсь распечатать результаты в следующем формате:

Item Name  | No. of Vers.  | User 
item_A  | 3     | dave, john 
item_B  | 1     | peter 
item_C  | 2     | jack, kris 

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

у меня возникли проблемы в Инте решетка в именах пользователей. Я использовал команду set(), и это кажется применимым для всех моих трех строк вывода. Несмотря на это, в то время как мои «Название товара» и «Нет. Верса. столбец, кажется правильным, есть ли способы, по которым я могу проверить, совпадает ли количество найденных версий с именем? Я могу считать это вручную, если у меня есть небольшие данные, но что, если я получу большие данные?

strip_ver_list = [] 
user_list = [] 
for item_name, user in gen_dict.iteritems(): 
    # Strip out the version digits 
    strip_ver = item_name[:-3] 
    strip_ver_list.append(strip_ver) 
    user_list.append(user) 


# This will count and remove the duplicates 
versions_num = dict((duplicate, strip_ver_list.count(duplicate)) for duplicate in strip_ver_list) 

for name, num in sorted(versions_num.iteritems()): 
    print "Version Name : {0}\nNo. of Versions : {1}\nUsers : {2}".format(name, num, set(user_list)) 

Это Ouput я получил:

Item Name  | No. of Vers.  | User 
item_A  | 3     | set(['dave', 'john', 'jack', 'kris', 'peter']) 
item_B  | 1     | set(['dave', 'john', 'jack', 'kris', 'peter']) 
item_C  | 2     | set(['dave', 'john', 'jack', 'kris', 'peter']) 

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

+1

Что такое 'duplicate'? – roganjosh

+0

Отредактировано, я пропустил часть. – dissidia

ответ

1

Я бы использовал defaultdict для агрегирования данных. Грубо говоря:

>>> from collections import defaultdict 
>>> gen_dict = { 
... "item_C_v001" : "jack", 
... "item_C_v002" : "kris", 
... "item_A_v003" : "john", 
... "item_B_v006" : "peter", 
... "item_A_v005" : "john", 
... "item_A_v004" : "dave" 
... } 

Теперь ...

>>> versions_num = defaultdict(lambda:dict(versions=set(), users = set())) 
>>> for item_name, user in gen_dict.items(): 
...  strip_ver = item_name[:-5] 
...  version_num = item_name[-3:] 
...  versions_num[strip_ver]['versions'].add(version_num) 
...  versions_num[strip_ver]['users'].add(user) 
... 

Наконец,

>>> for item, data in versions_num.items(): 
...  print("Item {} \tno. of Versions: {}\tUsers:{}".format(item, len(data['versions']), ",".join(data['users']))) 
... 
Item item_B  no. of Versions: 1  Users:peter 
Item item_A  no. of Versions: 3  Users:john,dave 
Item item_C  no. of Versions: 2  Users:kris,jack 
>>> 

И если вы хотите отсортирован:

>>> for item, data in sorted(versions_num.items()): 
...  print("Item {} \tno. of Versions: {}\tUsers:{}".format(item, len(data['versions']), ",".join(data['users']))) 
... 
Item item_A  no. of Versions: 3  Users:john,dave 
Item item_B  no. of Versions: 1  Users:peter 
Item item_C  no. of Versions: 2  Users:kris,jack 
+0

Я полагаю, что использование 'defaultdict' означает, что мне не нужно создавать новые dicts и позволяет мне« повторно использовать »его? – dissidia

+0

@dissidia Я не совсем уверен, что вы имеете в виду, но это звучит разумно ... Эта структура уже подталкивает то, что я считаю неуклюжим, и, возможно, стоит сделать это всем классом и инкапсулировать эту логику там. –

+0

Я делаю что-то подобное, но я пытаюсь подсчитать количество версий, которые каждый пользователь «занимает» на каждый ключ? Используя пример этого потока, 'item_C_v' имеет 2 общих элемента, но для отображения вывода для столбца пользователя как' jack (1), kris (1) ', это возможно? Я попытался использовать '[(k, len (list (v))) для k, v в itertools.groupby (отсортировано (gen_dict.values ​​()))]', но это будет отображать общее количество версий, которые каждый пользователь не принимают во внимание каждый ключ. – yan

1

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

from itertools import groupby 
# split the item_version 
sorted_ver_num = sorted(k.rsplit("_", 1) + [v] for k, v in gen_dict.items()) 

# group the results by the item name 
for k, g in groupby(sorted_ver_num, key = lambda x: x[0]): 
    # extract the user list within each group 
    # user_list = [user for *_, user in g] 
    user_list = [user for _, _, user in g] 
    print("Version Name : {0}\nNo. of Versions : {1}\nUsers : {2}".format(k, len(user_list), set(user_list))) 


Version Name : item_A 
No. of Versions : 3 
Users : {'dave', 'john'} 
Version Name : item_B 
No. of Versions : 1 
Users : {'peter'} 
Version Name : item_C 
No. of Versions : 2 
Users : {'kris', 'jack'} 
1

Я хотел бы использовать defaultdict держать трек пользователей и обычный дик t, чтобы отслеживать счет. Метод dict.get() позволяет вернуть значение по умолчанию, если ключ не найден, в данном случае 0, и вы просто добавляете 1 к нему каждый раз, когда ключ найден.

from collections import defaultdict 

gen_dict = { 
"item_C_v001" : "jack", 
"item_C_v002" : "kris", 
"item_A_v003" : "john", 
"item_B_v006" : "peter", 
"item_A_v005" : "john", 
"item_A_v004" : "dave" 
} 

user_dict = defaultdict(set) 
count_dict = {} 

for item_name, user in gen_dict.iteritems(): 
    user_dict[item_name[:-3]].add(user) # Sure you want -3 not -5? 
    count_dict[item_name[:-3]] = count_dict.get(item_name[:-3], 0) + 1 

for name, num in sorted(count_dict.iteritems()): 
    print "Version Name : {0}\nNo. of Versions : {1}\nUsers : {2}".format(
        name, num, ', '.join(item for item in user_dict[name])) 
+0

Я не знал, что целочисленное значение может быть в команде 'get' и использовать его как метод 'counter' для подсчета! – dissidia

+0

@dissidia метод 'get()' будет возвращать 'None', если ключ не найден, а не' KeyError' из 'dict []'. Но если вы не хотите 'None', как в этом случае, вы можете установить значение по умолчанию. В этом случае вы хотите запустить счетчик с '0'. Я нашел его быстрее, чем [Счетчик] (https://docs.python.org/2/library/collections.html#collections.Counter). См. [Здесь] (https://www.tutorialspoint.com/python/dictionary_get.htm) для получения дополнительной информации. – roganjosh

1

Пример в IPython:

In [1]: gen_dict = { 
    ...: "item_C_v001" : "jack", 
    ...: "item_C_v002" : "kris", 
    ...: "item_A_v003" : "john", 
    ...: "item_B_v006" : "peter", 
    ...: "item_A_v005" : "john", 
    ...: "item_A_v004" : "dave" 
    ...: } 

Получить ключи, мы будем нуждаться в них больше, чем один раз.

In [2]: keys = tuple(gen_dict.keys()) 

Найти набор предметов.

In [3]: items = set(j[:-5] for j in keys) 

Заголовок таблицы и шаблон.

In [4]: header = 'Item Name  | No. of Vers.  | User' 

In [5]: template = '{:14}|{:<15}|{}' 

Распечатать информацию по всем вопросам.

In [6]: print(header) 
Item Name  | No. of Vers.  | User 

In [7]: for i in items: 
    ...:  relevant = tuple(j for j in keys if j.startswith(i)) 
    ...:  users = set(gen_dict[x] for x in relevant) 
    ...:  print(template.format(i, len(relevant), ' '.join(users))) 
    ...:  
item_A  |3    |john dave 
item_B  |1    |peter 
item_C  |2    |kris jack