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)
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
.
Counter
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))
Où 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.
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)]
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
:
item[0]
ne figure pas dans la liste, mettez-le dans dict
avec count of 1
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
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 ]