Могут ли списочные представления Python (в идеале) сделать эквивалент «count (*) ... group by ...» в SQL? - PullRequest
5 голосов
/ 27 января 2010

Я думаю, что понимание списка может дать мне это, но я не уверен: какие-либо изящные решения в Python (2.6) в целом для выбора уникальных объектов в списке и обеспечения количества?

(я определил __eq__ для определения уникальности в моем определении объекта).

Итак, в RDBMS-стране, что-то вроде этого:

CREATE TABLE x(n NUMBER(1));
INSERT INTO x VALUES(1);
INSERT INTO x VALUES(1);
INSERT INTO x VALUES(1);
INSERT INTO x VALUES(2);

SELECT COUNT(*), n FROM x
GROUP BY n;

Что дает:

COUNT(*) n
==========
3        1
1        2

Итак, вот мой эквивалентный список в Python:

[1,1,1,2]

И я хочу получить тот же вывод, что и SQL SELECT, приведенный выше.

РЕДАКТИРОВАТЬ: пример, который я привел здесь, был упрощен, я на самом деле обрабатываю списки пользовательских экземпляров объектов: просто для полноты я включаю дополнительный код, который мне нужен, чтобы заставить все это работать:

import hashlib

def __hash__(self):
    md5=hashlib.md5()
    [md5.update(i) for i in self.my_list_of_stuff]
    return int(md5.hexdigest(),16)

Метод __hash__ был необходим для того, чтобы преобразовать set в работу (я выбрал идею понимания списка, которая работает в 2.6 [несмотря на то, что я узнал, что это связано с неэффективностью (см. Комментарии) - мои данные набор достаточно мал, чтобы это не было проблемой]). my_list_of_stuff выше - список (строк) в моем определении объекта.

Ответы [ 5 ]

11 голосов
/ 27 января 2010

Леннарт Регебро предоставил хороший однострочный , который делает то, что вы хотите:

>>> values = [1,1,1,2]
>>> print [(x,values.count(x)) for x in set(values)]
[(1, 3), (2, 1)]

Как отмечает С. Лотт , defaultdict может сделать то же самое.

11 голосов
/ 27 января 2010
>>> from collections import Counter
>>> Counter([1,1,1,2])
Counter({1: 3, 2: 1})

Счетчик доступен только в py3.1, наследуется от dict.

6 голосов
/ 27 января 2010

Не легко выполнимо как понимание списка.

from collections import defaultdict
def group_by( someList ):
    counts = defaultdict(int)
    for value in someList:
        counts[value.aKey] += 1
    return counts

Это очень Pythonic решение. Но не понимание списка.

4 голосов
/ 27 января 2010

Вы можете использовать groupby из модуля itertools:

Создать итератор, который возвращает последовательные ключи и группы из итерируемого.Ключ - это функция, вычисляющая значение ключа для каждого элемента.Если не указано или None, ключом по умолчанию является функция тождественности и возвращает элемент без изменений.Как правило, итерируемое уже должно быть отсортировано по той же ключевой функции.

>>> a = [1,1,1,2]
>>> [(len(list(v)), key) for (key, v) in itertools.groupby(sorted(a))]
[(3, 1), (1, 2)]

Я бы предположил, что его время выполнения хуже, чем у решений на основе dict от SilentGhost или S.Lott, поскольку онодолжен отсортировать входную последовательность, но вы должны рассчитать это самостоятельно.Это понимание списка, хотя.Это должно быть быстрее, чем решение Адама Бернье, так как ему не нужно выполнять повторное линейное сканирование входной последовательности.При необходимости можно избежать вызова sorted, отсортировав входную последовательность в строке.

1 голос
/ 26 августа 2011

Следующие работы в Python 2.4 и должны , поэтому работают в Python 2.6:

lst = [1,1,2,2,3,4,5,6,5]
lst_tmp = []
lst_dups = []

for item in lst:
    if item in lst_tmp:
        lst_dups.append(item)
    else:
        lst_tmp.append(item)

if len(lst_dups):
    lst_dups = sorted(set(lst_dups))
    for item in lst_dups:
        print str(lst.count(item)), "instances of", item
else:
    print "list is unique"
...