Я пишу код, который возвращает наиболее распространенный номер автобусной остановки в автобусном маршруте (из файла CSV). Первая запись в bus_routes.csv каждой строки - это название маршрута, а остальные записи - это номера автобусных остановок.

def most_common_number(routes):
    routes = load_routes('bus_routes.csv')
    bus_stop_ID = [col[1:] for col in routes]
    return max(sum(set((bus_stop_ID, []), key = sum(bus_stop_ID, []).count)

Когда я запускаю этот код, он возвращает ошибку; TypeError: может только объединить список (не "tuple") в список типа (bus_stop_number) - это список, поэтому я не могу найти, где кортеж существует в моем последнем коде.

3
rrr 12 Окт 2019 в 10:46
Вам, вероятно, понадобятся для этого collections.Counter и most_common вместо вашего кода, который пахнет ужасной сложностью. Но у нас нет ваших данных. Небольшой образец будет лучше, чем файл csv, которого у нас нет
 – 
Jean-François Fabre
12 Окт 2019 в 10:51
Сколько у вас столбцов? col [1:] может возвращать кортеж. Распечатайте отдельно и проверьте. А затем используйте коллекции. Счетчик, как сказал Жан.
 – 
Sharan
12 Окт 2019 в 10:52
1
Это явно не ваш реальный код; set не принимает аргумент key (или любые аргументы ключевого слова в этом отношении), и круглые скобки не уравновешиваются.
 – 
Karl Knechtel
12 Окт 2019 в 12:05

1 ответ

Я не могу найти, где кортеж существует в моем последнем коде

Каждый элемент bus_stop_ID является кортежем.

Напомним, у вас есть список кортежей переменной длины, и вы хотите найти самый распространенный элемент во всей этой структуре.

Похоже, это идеальное применение для collections.Counter :

>>> from collections import Counter
>>> bus_stop_ID = [('id1', 'id2', 'id3'), ('id1',), ('id1', 'id3', 'id5')]
>>> [(stop_id, count)] = sum(map(Counter, bus_stop_ID), Counter()).most_common(1)
>>> stop_id
'id1'
>>> count
3

Вот:

  • map(Counter, bus_stop_ID) подсчитывает идентификаторы автобусной остановки в каждом маршруте отдельно;
  • sum(..., Counter()) агрегирует подсчеты по маршрутам;
  • .most_common(1) возвращает наиболее распространенный идентификатор автобусной остановки в агрегированных счетах (в виде одноэлементного списка, содержащего 2-кортеж, который состоит из идентификатора автобусной остановки и его количества).

Альтернатива - сначала соединить все маршруты (используя sum()), а затем выполнить подсчет:

>>> [(stop_id, count)] = Counter(sum(bus_stop_ID, ())).most_common(1)

Это короче, но менее эффективно, поскольку необходимо объединить все маршруты в один (потенциально гигантский) кортеж.

Несколько необычный способ обойти эту неэффективность - использовать itertools.chain():

>>> Counter(itertools.chain(*bus_stop_ID)).most_common(1)
[('id1', 3)]

Это просто перебирает все идентификаторы автобусной остановки на первом маршруте, затем на втором и т. Д., Делая подсчет по пути.

Из любопытства я сравнил все три метода в произвольно выбранном списке из 10 000 кортежей, и результаты весьма интересны:

In [11]: %timeit sum(map(Counter, bus_stop_ID), Counter()).most_common(1)
1 loop, best of 3: 235 ms per loop

In [12]: %timeit Counter(sum(bus_stop_ID, ())).most_common(1)
1 loop, best of 3: 2.64 s per loop

In [13]: %timeit Counter(itertools.chain(*bus_stop_ID)).most_common(1)
10 loops, best of 3: 17.3 ms per loop
4
NPE 12 Окт 2019 в 11:15