Cuenta la frecuencia del artículo en una lista de tuplas

Tengo una lista de tuplas como se muestra a continuación. Tengo que contar cuántos artículos tienen un número mayor que 1. El código que he escrito hasta ahora es muy lento. Incluso si hay alrededor de 10K tuplas, si ves debajo la cadena de ejemplo aparece dos veces, entonces tengo que obtener ese tipo de cadenas. Mi pregunta es cuál es la mejor manera de lograr el recuento de cadenas aquí iterando sobre el generador

Lista:

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

Mi código hasta ahora:

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

Tienes la idea correcta de extraer el primer elemento de cada tupla. Puede hacer que su código sea más conciso usando una comprensión de lista / generador, como le muestro a continuación.

A partir de ese momento, la manera más idiomática de encontrar recuentos de frecuencia de elementos es mediante el uso de un objeto de collections.Counter .

  1. Extraiga los primeros elementos de su lista de tuplas (utilizando una comprensión)
  2. Pasar esto a Counter
  3. Recuento de consultas del example
 from collections import Counter counts = Counter(x[0] for x in b_data) print(counts['example']) 

Claro, puede usar list.count si solo es un elemento para el que desea encontrar conteos de frecuencia, pero en el caso general, un Counter es el camino a seguir.


La ventaja de un Counter es que realiza conteos de frecuencia de todos los elementos (no solo el example ) en tiempo lineal ( O(N) ). Supongamos que también desea consultar el recuento de otro elemento, digamos foo . Eso se haría con …

 print(counts['foo']) 

Si 'foo' no existe en la lista, se devuelve 0 .

Si desea encontrar los elementos más comunes, llame a counts.most_common

 print(counts.most_common(n)) 

Donde n es el número de elementos que desea mostrar. Si quieres ver todo, no pases n .


Para recuperar los conteos de los elementos más comunes, una forma eficiente de hacer esto es consultar most_common y luego extraer todos los elementos con conteos por encima de 1, de manera eficiente con 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)] 

(Edición OP) Alternativamente, use una lista de comprensión para obtener una lista de elementos que cuentan> 1 –

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

Tenga en cuenta que esto no es tan eficiente como la solución itertools.takewhile . Por ejemplo, si tiene un artículo con cuenta> 1 y un millón de artículos con cuenta igual a 1, terminará iterando sobre la lista un millón y una vez, cuando no tenga que hacerlo (porque most_common devuelve la frecuencia cuenta en orden descendente). Con takewhile ese no es el caso, porque deja de iterar tan pronto como la condición de conteo> 1 se vuelve falsa.

Primer método:

¿Qué pasa sin bucle?

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

salida:

 2 

Segundo método:

Puede calcular utilizando un dict simple, sin importar ningún módulo externo o sin hacerlo tan complejo:

 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()))))) 

salida:

 [('example', 2)] 

Caso de prueba :

 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)] 

salida:

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

El tiempo que tardé en hacer este ayodhyankit-paul se publicó de la misma manera, dejándolo en menos para el código del generador para los testcases y la sincronización

La creación de 100001 artículos tomó aproximadamente 5 segundos, el conteo tomó aproximadamente 0,3s , el filtrado por conteos fue demasiado rápido para medir (con datetime.now () – no se molestó con perf_counter ) – en total tomó menos de 5,1 segundos desde el principio hasta el final por alrededor de 10 veces los datos que opera.

Creo que esto es similar a lo que hace Counter en la respuesta de COLDSPEED :

Para cada item en la list of tuples :

  • Si el item[0] no está en la lista, póngalo en dict con un count of 1
  • otra increment count en dict by 1

Código:

 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" ) 

Salida:

 # 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 

Déjame darte un ejemplo para que entiendas. Aunque este ejemplo es muy diferente al de tu ejemplo, lo encontré muy útil al resolver este tipo de preguntas.

 from collections import Counter a = [ (0, "Hadoop"), (0, "Big Data"), (0, "HBase"), (0, "Java"), (1, "Postgres"), (2, "Python"), (2, "scikit-learn"), (2, "scipy"), (2, "numpy"), (2, "statsmodels"), (2, "pandas"), (3, "R"), (3, "Python"), (3, "statistics"), (3, "regression"), (3, "probability"), (4, "machine learning"), (4, "regression"), (4, "decision trees"), (4, "libsvm"), (5, "Python"), (5, "R"), (5, "Java"), (5, "C++"), (5, "Haskell"), (5, "programming languages"), (6, "statistics"), (6, "probability"), (6, "mathematics"), (6, "theory"), (7, "machine learning"), (7, "scikit-learn"), (7, "Mahout"), (7, "neural networks"), (8, "neural networks"), (8, "deep learning"), (8, "Big Data"), (8, "artificial intelligence"), (9, "Hadoop"), (9, "Java"), (9, "MapReduce"), (9, "Big Data") ] # # 1. Lowercase everything # 2. Split it into words. # 3. Count the results. dictionary = Counter(word for i, j in a for word in j.lower().split()) print(dictionary) # print out every words if the count > 1 [print(word, count) for word, count in dictionary.most_common() if count > 1] 

Ahora este es tu ejemplo resuelto de la manera anterior

 from collections import Counter a=[('example',123),('example-one',456),('example',987),('example2',987),('example3',987)] dict = Counter(word for i,j in a for word in i.lower().split() ) print(dict) [print(word ,count) for word,count in dict.most_common() if count > 1 ]