Как получить количество предметов, но сохранить порядок, в котором они появляются?

Например, мне нужно подсчитать, сколько раз слово появляется в списке, не отсортированном по частоте, а с порядком, в котором слова появляются, то есть порядком вставки.

from collections import Counter

words = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']

c = Counter(words)

print(c)

Итак, вместо: {'apples': 3, 'kiwis': 2, 'bananas': 1, 'oranges': 1}

Я бы предпочел: {'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

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


person rsk82    schedule 19.05.2014    source источник
comment
По теме: Создание заказанного счетчика   -  person jpp    schedule 05.09.2018


Ответы (4)


Вы можете использовать рецепт, в котором используются collections.Counter и collections.OrderedDict:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
c = OrderedCounter(words)
print(c)
# OrderedCounter(OrderedDict([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)]))
person Jon Clements♦    schedule 19.05.2014

В Python 3.6+ dict теперь будет поддерживать порядок вставки.

Итак, вы можете:

words = ["oranges", "apples", "apples", "bananas", "kiwis", "kiwis", "apples"]
counter={}
for w in words: counter[w]=counter.get(w, 0)+1
>>> counter
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

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

Но вы можете использовать тот же рецепт OrderedDict, но просто используйте вместо этого Python 3.6+:

from collections import Counter

class OrderedCounter(Counter, dict):
    'Counter that remembers the order elements are first encountered'
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, dict(self))

    def __reduce__(self):
        return self.__class__, (dict(self),)

>>> OrderedCounter(words)
OrderedCounter({'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2})

Или, поскольку Counter является подклассом dict, который поддерживает порядок в Python 3.6+, вы можете просто избежать использования __repr__ Counter, либо вызвав .items() на счетчике, либо превратив счетчик обратно в dict:

>>> c=Counter(words)

Это представление этого счетчика отсортировано по наиболее частым элементам до наименьшего и использует метод счетчиков __repr__:

>>> c
Counter({'apples': 3, 'kiwis': 2, 'oranges': 1, 'bananas': 1})

Эта презентация соответствует встречному или порядку вставки:

>>> c.items()
dict_items([('oranges', 1), ('apples', 3), ('bananas', 1), ('kiwis', 2)])

Or,

>>> dict(c)
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}
person dawg    schedule 14.07.2018

В Python 3.6 словари упорядочены по вставке, но это деталь реализации.

В Python 3.7+ порядок размещения гарантирован, и на него можно положиться. См. Упорядочены ли словари в Python 3.6+? для получения дополнительных сведений.

Итак, в зависимости от вашей версии Python вы можете просто использовать Counter как есть, не создавая OrderedCounter класс, как описано в документация. Это работает, потому что Counter является подклассом dict, т.е. issubclass(Counter, dict) возвращает True и, следовательно, наследует поведение порядка вставки dict.

Строковое представление

Стоит отметить строковое представление для Counter, как определено в методе repr, не обновлялся, чтобы отразить изменение в 3.6 / 3.7, т. Е. print(Counter(some_iterable)) по-прежнему возвращает элементы из самых больших по убыванию. Вы можете легко вернуть заказ на размещение через list(Counter(some_iterable)).

Вот несколько примеров, демонстрирующих поведение:

x = 'xyyxy'
print(Counter(x))         # Counter({'y': 3, 'x': 2}), i.e. most common first
print(list(Counter(x)))   # ['x', 'y'], i.e. insertion ordered
print(OrderedCounter(x))  # OC(OD([('x', 2), ('y', 3)])), i.e. insertion ordered

Исключения

Не следует использовать обычный Counter, если для вас важны дополнительные или перезаписанные методы, доступные для OrderedCounter. Особо следует отметить:

  1. OrderedDict и, следовательно, OrderedCounter предлагают popitem и _ 18_ методы.
  2. Проверки равенства между OrderedCounter объектами чувствительны к порядку и реализованы как list(oc1.items()) == list(oc2.items()).

Например, тесты на равенство дадут разные результаты:

Counter('xy') == Counter('yx')                # True
OrderedCounter('xy') == OrderedCounter('yx')  # False
person jpp    schedule 04.09.2018

Объяснено в комментариях

text_list = ['oranges', 'apples', 'apples', 'bananas', 'kiwis', 'kiwis', 'apples']


# create empty dictionary
freq_dict = {}
 
# loop through text and count words
for word in text_list:
    # set the default value to 0
    freq_dict.setdefault(word, 0)
    # increment the value by 1
    freq_dict[word] += 1
 
print(freq_dict )
{'oranges': 1, 'apples': 3, 'bananas': 1, 'kiwis': 2}

[Program finished]
person Subham    schedule 29.03.2021