Считать частоту элемента в списке кортежей

У меня есть список кортежей, как показано ниже. Я должен подсчитать, сколько элементов имеет число больше 1. Код, который я написал до сих пор, очень медленный. Даже если есть около 10K кортежей, если вы видите ниже строку примера, появляется два раза, поэтому мне нужно получить такие строки. Мой вопрос заключается в том, что лучший способ достичь количества строк здесь путем итерации по генератору

Список:

 b_data=[('example',123),('example-one',456),('example',987),.....]

Мой код:

blockslst=[]
for line in b_data:
    blockslst.append(line[0])

blocklstgtone=[]
for item in blockslst:
    if(blockslst.count(item)>1):
        blocklstgtone.append(item)

Ответ 1

У вас есть правильная идея, извлекающая первый элемент из каждого кортежа. Вы можете сделать код более кратким, используя понимание списка/генератора, как я покажу вам ниже.

С этого момента самый идиоматический способ поиска частотных элементов элементов использует объект collections.Counter.

  • Извлеките первые элементы из списка кортежей (используя понимание)
  • Передайте это Counter
  • Количество запросов example
from collections import Counter

counts = Counter(x[0] for x in b_data)
print(counts['example'])

Конечно, вы можете использовать list.count, если только один элемент, на который вы хотите найти частоту, учитывается, но в общем случае это путь Counter.


Преимущество Counter заключается в том, что он выполняет подсчет частоты всех элементов (а не только example) в линейном (O(N)) времени. Скажем, вы также хотели запросить счет другого элемента, скажем foo. Это будет сделано с помощью

print(counts['foo'])

Если 'foo' не существует в списке, возвращается 0.

Если вы хотите найти наиболее распространенные элементы, вызовите counts.most_common -

print(counts.most_common(n))

Где n - количество элементов, которые вы хотите отобразить. Если вы хотите увидеть все, не проходите n.


Чтобы получить подсчеты большинства общих элементов, один эффективный способ сделать это - запросить most_common, а затем извлечь все элементы с числами более 1, эффективно с помощью itertools.

from itertools import takewhile

l = [1, 1, 2, 2, 3, 3, 1, 1, 5, 4, 6, 7, 7, 8, 3, 3, 2, 1]
c = Counter(l)

list(takewhile(lambda x: x[-1] > 1, c.most_common()))
[(1, 5), (3, 4), (2, 3), (7, 2)]

(Редактирование OP) В качестве альтернативы, используйте представление списка, чтобы получить список элементов, имеющих count > 1 -

[item[0] for item in counts.most_common() if item[-1] > 1]

Имейте в виду, что это не так эффективно, как решение itertools.takewhile. Например, если у вас есть один элемент с count > 1 и миллион элементов со счетом, равным 1, youd заканчивает итерирование по списку миллион и один раз, когда вам не нужно (потому что most_common возвращает частоту в в порядке убывания). С takewhile это не так, потому что вы прекратите итерацию, как только условие count > 1 станет false.

Ответ 2

Первый метод:

Как насчет без цикла?

print(list(map(lambda x:x[0],b_data)).count('example'))

выход:

2

Второй метод:

Вы можете рассчитать, используя простой dict, без импорта какого-либо внешнего модуля или без его создания:

b_data = [('example', 123), ('example-one', 456), ('example', 987)]

dict_1={}
for i in b_data:
    if i[0] not in dict_1:
        dict_1[i[0]]=1
    else:
        dict_1[i[0]]+=1

print(dict_1)



print(list(filter(lambda y:y!=None,(map(lambda x:(x,dict_1.get(x)) if dict_1.get(x)>1 else None,dict_1.keys())))))

выход:

[('example', 2)]

Test_case:

b_data = [('example', 123), ('example-one', 456), ('example', 987),('example-one', 456),('example-one', 456),('example-two', 456),('example-two', 456),('example-two', 456),('example-two', 456)]

выход:

[('example-two', 4), ('example-one', 3), ('example', 2)]

Ответ 3

Время, которое потребовалось мне сделать ayodhyankit-paulотправил то же самое - оставив его, тем не менее, для кода генератора для тестовых мест и времени:

Создание элементов 100001 заняло примерно 5 секунд, подсчет занял около 0,3 с, фильтрация по подсчетам была слишком быстрой для измерения (с datetime.now() - не беспокоился perf_counter) - все это потребовалось менее 5.1s от начала до конца примерно в 10 раз больше данных, которые вы используете.

Я думаю, что это похоже на то, что Counter в COLDSPEED отвечает:

foreach item в list of tuples:

  • если item[0] нет в списке, введите dict с count of 1
  • else increment count в dict by 1

код:

from collections import Counter
import random
from datetime import datetime # good enough for a loong running op


dt_datagen = datetime.now()
numberOfKeys = 100000 


# basis for testdata
textData = ["example", "pose", "text","someone"]
numData = [random.randint(100,1000) for _ in range(1,10)] # irrelevant

# create random testdata from above lists
tData = [(random.choice(textData)+str(a%10),random.choice(numData)) for a in range(numberOfKeys)] 

tData.append(("aaa",99))

dt_dictioning = datetime.now()

# create a dict
countEm = {}

# put all your data into dict, counting them
for p in tData:
    if p[0] in countEm:
        countEm[p[0]] += 1
    else:
        countEm[p[0]] = 1

dt_filtering = datetime.now()
#comparison result-wise (commented out)        
#counts = Counter(x[0] for x in tData)
#for c in sorted(counts):
#    print(c, " = ", counts[c])
#print()  
# output dict if count > 1
subList = [x for x in countEm if countEm[x] > 1] # without "aaa"

dt_printing = datetime.now()

for c in sorted(subList):
    if (countEm[c] > 1):
        print(c, " = ", countEm[c])

dt_end = datetime.now()

print( "\n\nCreating ", len(tData) , " testdataitems took:\t", (dt_dictioning-dt_datagen).total_seconds(), " seconds")
print( "Putting them into dictionary took \t", (dt_filtering-dt_dictioning).total_seconds(), " seconds")
print( "Filtering donw to those > 1 hits took \t", (dt_printing-dt_filtering).total_seconds(), " seconds")
print( "Printing all the items left took    \t", (dt_end-dt_printing).total_seconds(), " seconds")

print( "\nTotal time: \t", (dt_end- dt_datagen).total_seconds(), " seconds" )

Вывод:

# reformatted for bevity
example0  =  2520       example1  =  2535       example2  =  2415
example3  =  2511       example4  =  2511       example5  =  2444
example6  =  2517       example7  =  2467       example8  =  2482
example9  =  2501

pose0  =  2528          pose1  =  2449          pose2  =  2520      
pose3  =  2503          pose4  =  2531          pose5  =  2546          
pose6  =  2511          pose7  =  2452          pose8  =  2538          
pose9  =  2554

someone0  =  2498       someone1  =  2521       someone2  =  2527
someone3  =  2456       someone4  =  2399       someone5  =  2487
someone6  =  2463       someone7  =  2589       someone8  =  2404
someone9  =  2543

text0  =  2454          text1  =  2495          text2  =  2538
text3  =  2530          text4  =  2559          text5  =  2523      
text6  =  2509          text7  =  2492          text8  =  2576      
text9  =  2402


Creating  100001  testdataitems took:    4.728604  seconds
Putting them into dictionary took        0.273245  seconds
Filtering donw to those > 1 hits took    0.0  seconds
Printing all the items left took         0.031234  seconds

Total time:      5.033083  seconds