Ранжирование элементов нескольких списков по их количеству в Python

Я хочу ранжировать несколько списков в соответствии с их элементами, как часто они появляются в каждом списке. Пример:

список1 = 1,2,3,4
список2 = 4,5,6,7
список3 = 4,1,8,9

результат = 4,1,2,3,4,5,6,7,8 (4 считается три раза, 1 два раза и остальные один раз)

Я пробовал следующее, но мне нужно что-то более интеллектуальное и что-то, что я могу сделать с любым количеством списков.


 l = []
 l.append([ 1, 2, 3, 4, 5])
 l.append([ 1, 9, 3, 4, 5])
 l.append([ 1, 10, 8, 4, 5])
 l.append([ 1, 12, 13, 7, 5])
 l.append([ 1, 14, 13, 13, 6])

 x1 = set(l[0]) & set(l[1]) & set(l[2]) & set(l[3])
 x2 = set(l[0]) & set(l[1]) & set(l[2]) & set(l[4])
 x3 = set(l[0]) & set(l[1]) & set(l[3]) & set(l[4])
 x4 = set(l[0]) & set(l[2]) & set(l[3]) & set(l[4])
 x5 = set(l[1]) & set(l[2]) & set(l[3]) & set(l[4])
 set1 = set(x1) | set(x2) | set(x3) | set(x4) | set(x5)

 a1 = list(set(l[0]) & set(l[1]) & set(l[2]) & set(l[3]) & set(l[4]))
 a2 = getDifference(list(set1),a1)
 print a1
 print a2

Теперь вот проблема ... я могу делать это снова и снова с a3, a4 и a5, но тогда это слишком сложно, мне нужна функция для этого ... Но я не знаю, как ... моя математика застряла ;)

РЕШЕНО: Большое спасибо за обсуждение. Мне, как новичку, эта система чем-то нравится: быстро+информативно. Вы мне во всем помогли! Тай


person Tom Siwik    schedule 01.12.2009    source источник


Ответы (6)


import collections

data = [
  [1, 2, 3, 4, 5],
  [1, 9, 3, 4, 5],
  [1, 10, 8, 4, 5],
  [1, 12, 13, 7, 5],
  [1, 14, 13, 13, 6],
]

def sorted_by_count(lists):
  counts = collections.defaultdict(int)
  for L in lists:
    for n in L:
      counts[n] += 1

  return [num for num, count in
          sorted(counts.items(),
                 key=lambda k_v: (k_v[1], k_v[0]),
                 reverse=True)]

print sorted_by_count(data)

Теперь давайте обобщим его (чтобы взять любое итерируемое, ослабленное требование к хешированию), разрешим ключевые и обратные параметры (для соответствия отсортированному) и переименуем в freq_sorted:

def freq_sorted(iterable, key=None, reverse=False, include_freq=False):
  """Return a list of items from iterable sorted by frequency.

  If include_freq, (item, freq) is returned instead of item.

  key(item) must be hashable, but items need not be.

  *Higher* frequencies are returned first.  Within the same frequency group,
  items are ordered according to key(item).
  """
  if key is None:
    key = lambda x: x

  key_counts = collections.defaultdict(int)
  items = {}
  for n in iterable:
    k = key(n)
    key_counts[k] += 1
    items.setdefault(k, n)

  if include_freq:
    def get_item(k, c):
      return items[k], c
  else:
    def get_item(k, c):
      return items[k]

  return [get_item(k, c) for k, c in
          sorted(key_counts.items(),
                 key=lambda kc: (-kc[1], kc[0]),
                 reverse=reverse)]

Пример:

>>> import itertools
>>> print freq_sorted(itertools.chain.from_iterable(data))
[1, 5, 4, 13, 3, 2, 6, 7, 8, 9, 10, 12, 14]
>>> print freq_sorted(itertools.chain.from_iterable(data), include_freq=True)
# (slightly reformatted)
[(1, 5),
 (5, 4),
 (4, 3), (13, 3),
 (3, 2),
 (2, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (12, 1), (14, 1)]
person Community    schedule 01.12.2009
comment
Я не могу решить, какое решение является самым быстрым. 2 for's = O(n**2), но как сделать это быстрее.. :/ - person Tom Siwik; 02.12.2009
comment
Бьет меня. Ваш единственный (кроме моего, но я скопировал ваш), который применяет второй порядок сортировки, что приятно. - person Robert Rossney; 02.12.2009
comment
Два цикла for - это не то, что делает некоторые из этих ответов O (n ^ 2), чего нет в этом. Он вызывает count(x) для каждого элемента в цепочке списка, что делает их O(n^2). - person Robert Rossney; 02.12.2009
comment
proxylittle: n, являющийся суммой длин каждого списка в lists, означает, что мои вложенные циклы for по-прежнему составляют только O(n), или каждый элемент исходных данных обрабатывается только один раз (грубое упрощение, не совсем верно, но O(2n ) по-прежнему O (n)). Кроме того, сортировка — O (m log m), а окончательное понимание списка — O (m) (где m — количество уникальных элементов), поэтому вся функция — O (n log n) (m не может быть больше n) . Тем не менее, самое быстрое решение по-прежнему зависит от того, как именно оно реализовано, и от характеристик входных данных, но алгоритмическая сложность — это то, как вы это обобщаете. - person ; 02.12.2009

Объединяю пару уже опубликованных идей:

from itertools import chain
from collections import defaultdict

def frequency(*lists):
    counter = defaultdict(int)
    for x in chain(*lists):
        counter[x] += 1
    return [key for (key, value) in 
        sorted(counter.items(), key=lambda kv: (kv[1], kv[0]), reverse=True)]

Заметки:

  1. В Python 2.7 вы можете использовать Counter вместо defaultdict(int).
  2. Эта версия принимает в качестве аргумента любое количество списков; ведущая звездочка означает, что все они будут упакованы в кортеж. Если вы хотите передать один список, содержащий все ваши списки, опустите эту начальную звездочку.
  3. Это ломается, если ваши списки содержат нехешируемый тип.
person Robert Rossney    schedule 01.12.2009

Попробуй это:

def rank(*lists):
    d = dict()
    for lst in lists:
        for e in lst:
            if e in d: d[e] += 1
            else: d[e] = 1
    return [j[1] for j in sorted([(d[i],i) for i in d], reverse=True)]

Пример использования:

a = [1,2,3,4]
b = [4,5,6,7]
c = [4,1,8,9]

print rank(a,b,c)

Вы можете использовать любое количество списков в качестве входных данных

person Andrea Ambu    schedule 01.12.2009

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

def histogram(enumerable):
  result = {}
  for x in enumerable:
    result.setdefault(x, 0)
    result[x] += 1
  return result

lists = [ [1,2,3,4], [4,5,6,7], ... ]

from itertools import chain

h = histogram(chain(*lists))
ranked = sorted(set(chain(*lists)), key = lambda x : h[x], reverse = True)
person orip    schedule 01.12.2009
comment
Здесь вы дважды повторяете цепочку списков. Если вы сортируете гистограмму, вам нужно выполнить итерацию только один раз. (Кроме того, вам не нужно создавать набор, потому что вы уже это сделали.) - person Robert Rossney; 02.12.2009
comment
@Robert - вы правы, но эффективность - не единственная проблема. Я нахожу использование ключей гистограммы запутанным - конечно, если бы это было узким местом, то, конечно, я бы не колебался. - person orip; 02.12.2009

Попробуйте этот код:

def elementFreq(myList):
    #myList is the list of lists
    from collections import Counter
    tmp = []
    for i in myList: tmp += i        
    return(Counter(tmp))

Примечание. Ваши списки должны быть хэшируемыми.

person Ashutosh    schedule 30.10.2018

person    schedule
comment
Старый добрый О(n**2), ах, как я по тебе соскучилась. - person ; 02.12.2009
comment
Он не сказал, что хочет, чтобы она быстро осветилась. Это просто должно было быть просто. - person twneale; 02.12.2009
comment
Верно, но проблема с O(n^2) заключается в том, что прыжок от не молниеносного к неприемлемо медленному сделать действительно легко. - person Robert Rossney; 02.12.2009
comment
ах.. ну... чем быстрее, тем лучше. - person Tom Siwik; 02.12.2009