web-dev-qa-db-fra.com

Compter la fréquence de l'article dans une liste de tuples

J'ai une liste de tuples comme indiqué ci-dessous. Je dois compter combien d'éléments ont un nombre supérieur à 1. Le code que j'ai écrit jusqu'à présent est très lent. Même s'il y a environ 10K tuples, si vous voyez ci-dessous un exemple, une chaîne apparaît deux fois. Je dois donc obtenir ce type de chaîne. Ma question est quelle est la meilleure façon de réaliser le nombre de chaînes ici en itérant sur le générateur

Liste:

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

Mon code jusqu'ici:

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

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

Vous avez la bonne idée d'extraire le premier élément de chaque tuple. Vous pouvez rendre votre code plus concis en utilisant une compréhension liste/générateur, comme je vous le montre ci-dessous.

À partir de ce moment, la manière la plus idiomatique de trouver des comptes de fréquence d'éléments consiste à utiliser un objet collections.Counter.

  1. Extrayez les premiers éléments de votre liste de tuples (en utilisant une compréhension)
  2. Passez ceci à Counter
  3. Nombre de requêtes de example
from collections import Counter

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

Bien sûr, vous pouvez utiliser list.count s’il ne s’agit que de one élément pour lequel vous voulez trouver des comptes de fréquence, mais dans le cas général, une Counter est le chemin à parcourir.


L’avantage d’une Counter est qu’elle effectue des comptages de fréquence de tous éléments (pas seulement example) en temps linéaire (O(N)). Supposons que vous souhaitiez également interroger le nombre d'un autre élément, par exemple, foo. Cela se ferait avec -

print(counts['foo'])

Si 'foo' n’existe pas dans la liste, 0 est renvoyé.

Si vous voulez trouver les éléments les plus communs, appelez counts.most_common

print(counts.most_common(n))

n est le nombre d'éléments que vous souhaitez afficher. Si vous voulez tout voir, ne passez pas n.


Pour extraire le nombre d'éléments les plus communs, une méthode efficace consiste à interroger most_common, puis à extraire tous les éléments dont le nombre est supérieur à 1, de manière efficace avec 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 edit) Vous pouvez également utiliser la liste comprehrehension pour obtenir une liste des éléments ayant un nombre> 1 - 

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

Gardez à l’esprit que ce n’est pas aussi efficace que la solution itertools.takewhile. Par exemple, si vous avez un élément dont le nombre est supérieur à 1 et un million d’éléments dont le nombre est égal à 1, vous finissez par parcourir la liste un million et une fois sans avoir à le faire (car most_common renvoie la fréquence compte en ordre décroissant). Avec takewhile, ce n’est pas le cas, car vous arrêtez d’itérer dès que la condition de nombre> 1 devient fausse.

12
coldspeed

Première méthode:

Qu'en est-il sans boucle?

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

sortie:

2

Deuxième méthode:

Vous pouvez calculer en utilisant dict simple, sans importer de module externe ou sans le rendre si complexe:

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

sortie:

[('example', 2)]

Cas de test :

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

sortie:

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

Il m'a fallu du temps pour le faire ayodhyankit-paul posté le même - le laissant dans non moins pour le code de générateur pour les tests et le timing:

La création d'éléments 100001 prenait environ 5 secondes et le comptage prenait environ 0.3s, Le filtrage des comptes était trop rapide pour être mesuré (avec datetime.now () - ne me dérangeait pas avec perf_counter ) - Au total, cela a pris moins de 5.1s du début à la fin pour environ 10 fois plus de données que celles sur lesquelles vous opérez.

Je pense que cela ressemble à ce que Counter dans COLDSPEED s réponse fait: 

foreach item in list of tuples:

  • si item[0] ne figure pas dans la liste, mettez-le dans dict avec count of 1
  • sinon increment count dans dict by 1

Code:

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

Sortie:

# 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 
2
Patrick Artner

Laissez-moi vous donner un exemple pour vous faire comprendre. Bien que cet exemple soit très différent de votre exemple, je l’ai trouvé très utile pour résoudre ce type de questions.

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]

Maintenant, voici votre exemple résolu de la manière ci-dessus

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  ]
0
Soudipta Dutta