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

Я пытаюсь распечатать пару "Значение ключа" в том же порядке, что и в выходных данных OrderedCounter.

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    pass

c = OrderedCounter('supernatural')
print c

Я получаю следующий вывод:

OrderedCounter({'u': 2, 'r': 2, 'a': 2, 's': 1, 'p': 1, 'e': 1, 'n': 1, 't': 1, 'l': 1})

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

Я в основном пытаюсь напечатать первый повторяющийся символ в данной строке.

4 ответа

Проблема в том, что __repr__ используется первым суперклассом (потому что вы не переопределяете его), и это Counter, Представление Counter является то, что это отсортировано по значениям в порядке убывания. Тот факт, что вы подкласс OrderedDict а также sorted стабильным, создается впечатление, что "u" это первый элемент.

тем не мение Counter не обеспечивает __iter__ метод, так что вы будете использовать __iter__ из OrderedDict который просто сохраняет порядок вставки:

>>> next(iter(c.items()))
('s', 1)

Чтобы получить первый повторяющийся символ, просто используйте понимание:

>>> next((key, value) for key, value in c.items() if value > 1)
('u', 2)

(С Python2 вы, вероятно, хотите использовать iteritems() вместо items())

Для печати первого наиболее распространенного значения вы можете использовать Counter.most_common метод:

>>> c.most_common(1)
[('u', 2)]

Вам не нужно Count или же OrderedDict для этой задачи. Вот оптимизированный подход (для строки длины n сложность O(n)):

In [35]: def first_repeated(s):
             seen = set()
             for i, j in enumerate(s):
                if j in seen: # membership check in set is O(1)
                    return j, s.count(j, i + 1) + 2 
                seen.add(j)
   ....:         

In [36]: first_repeated(s)
Out[36]: ('u', 2)

Вот тест с другим ответом, который показывает, что этот метод почти в 4-5 раз быстрее:

In [39]: def counter_based(s):
   ....:     c = Counter(s)
   ....:     return next(key for key in c if c[key] > 1)
   ....: 

In [40]: %timeit counter_based(s)
100000 loops, best of 3: 5.09 us per loop

In [41]: %timeit first_repeated(s)
1000000 loops, best of 3: 1.71 us per loop

Также вы можете выполнить эту задачу еще быстрее, используя дерево суффиксов, особенно если вы хотите выполнить его с большим объемом данных. Вот оптимизированная реализация этого алгоритма мной в github. Вы также можете использовать документацию и полезные ссылки, если вы не знакомы с этой структурой данных и алгоритмом https://github.com/kasramvd/SuffixTree

В качестве другого линейного ответа используется str.counter внутри выражения генератора вы можете использовать следующий подход, предложенный @Stefan Pochmann:

next((c, s.count(c)) for c in s if s.count(c) > 1)

Из того, что я понимаю, я думаю, что вы ищете что-то вроде этого:

print c.most_common()[0]

Это дает вывод ('u', 2)

Если вам нужен счетчик где-то внизу строки, можно отфильтровать и отсортировать его, чтобы получить то, что вы ищете:

from collections import Counter

input_string = 'supernatural'
c = Counter(input_string)
print sorted((pair for pair in c.items() if pair[1]>1), key=lambda x: input_string.index(x[0]))[0]

Мы фильтруем счетчик так, чтобы он возвращал только те буквы, которые появляются более одного раза, сортируем его в соответствии с его положением во входной строке и возвращаем первую найденную пару. Следовательно, это печатает ('u', 2)

Другие вопросы по тегам