การจัดอันดับองค์ประกอบของหลายรายการตามจำนวนใน 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 นับเป็น 3 ครั้ง 1 สองครั้ง ที่เหลือ 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
two for loops ไม่ใช่สิ่งที่ทำให้คำตอบบางข้อ O(n^2) ซึ่งอันนี้ไม่ใช่ เป็นการเรียก count(x) ในแต่ละรายการในรายการลูกโซ่ที่ทำให้พวกเขาเป็น O(n^2) - person Robert Rossney; 02.12.2009
comment
proxylittle: n เป็นผลรวมของความยาวของแต่ละรายการใน lists หมายความว่า for loops ที่ซ้อนกันของฉันยังคงอยู่เพียง 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
Good ol' O(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