1
votes

Gardez tous les éléments dans une liste à partir d'une autre

J'ai deux grandes listes train et keep , cette dernière contenant des éléments uniques , par exemple XXX

Existe-t-il un moyen de créer une nouvelle liste contenant tous les éléments de train qui sont dans keep code > en utilisant des sets ? Le résultat final doit être:

train_keep = [1, 3, 4, 3, 1]

Actuellement, j'utilise itertools.filterfalse de comment conserver les éléments d'une liste basée sur une autre liste mais c'est très lent car les listes sont grandes ...


6 commentaires

Set n'autorisera pas les doublons


@Dead Pool. Trois réponses identiques disent que ça va :)


@Deadpool keep contient des éléments uniques, je veux juste garder les doublons dans train . N'est-ce pas possible avec les méthodes set ?


Si vous dites ci-dessous que les réponses ne sont pas à la hauteur, vous devez casser votre ensemble de données et examiner une certaine concurreny, pour moi, il semble que cette solution n'est pas possible avec les ensembles @alex_lewis


J'ai mis à jour ma réponse avec une suggestion.


J'ai mis à jour ma réponse avec une meilleure suggestion


5 Réponses :


0
votes

c'est une option:

train = [1, 2, 3, 4, 5, 5, 5, 5, 3, 2, 1]
keep = [1, 3, 4]

keep_set = set(keep)
res = [item for item in train if item in keep_set]
# [1, 3, 4, 3, 1]

j'utilise keep_set afin d'accélérer un peu la recherche.


6 commentaires

Le fait-il cependant? Accélérez la recherche, je veux dire.


Je pense que je me souviens d'un exemple où il y avait une accélération pour seulement 2 éléments (mais je ne pouvais pas vous donner de pointeur ni n'ai exécuté de benchmark avec un interpréteur python actuel) ...


Une chose à laquelle vous devez faire attention est que le set (keep) est évalué pour chaque itération de la boucle, par exemple: (chaque élément dans train ) ... Par conséquent, cela vaut la peine de le déplacer hors de la liste-comp


@JonClements bien sûr, en général, cela devrait être fait. (pourrait adapter sa réponse) ...


Oh, même si vous l'exécutez une fois, vous créez cet ensemble len (train) fois à chaque itération de la liste-comp ...


par exemple ... essayez ... def my_set (iterable): print ('called'); return set (iterable) ... puis utilisez my_set comme appel de fonction dans votre liste-comp au lieu de set ...



1
votes
>>> keep_set = set(keep)
>>> [val for val in train if val in keep_set]
[1, 3, 4, 3, 1]
Note that if keep is small, there might not be any performance advantage to converting it to a set (benchmark to make sure).

3 commentaires

Puisque OP dit que c'est très lent, je suppose que leurs deux ensembles de données réels sont un peu plus grands que ce qui est indiqué.


@alex_lewis: Alors la question ne nous donne pas suffisamment d'informations pour fournir une bonne réponse. Un MCVE des problèmes de performances pourrait être un bon début: stackoverflow.com/help/minimal-reproducible-example


@alex_lewis. Vous n'obtiendrez pas beaucoup d'accélération avec Vanilla Python



3
votes

Convertissez la liste keep en un set , car cela sera vérifié fréquemment. Itérez sur train , puisque vous voulez garder l'ordre et répéter. Cela fait de set pas une option. Même si c'était le cas, cela n'aiderait pas, car l'itération devrait de toute façon se produire:

train = np.array([...])
keep = np.array([... 99999])
keep.sort()

ind = np.searchsorted(keep, train, side='left')
train_keep = train[keep[ind] == train]

Une version plus paresseuse et probablement plus lente serait quelque chose comme

train = np.array([...])
keep = np.array([...])
keep.sort()

ind = np.searchsorted(keep, train, side='left')
ind[ind == keep.size] -= 1
train_keep = train[keep[ind] == train]

Aucune de ces options ne vous donnera une accélération importante, vous feriez probablement mieux d'utiliser numpy ou pandas ou une autre bibliothèque qui implémente les boucles en C et stocke les nombres comme quelque chose de plus simple que des objets python à part entière . Voici un exemple de solution numpy:

train = np.array([...])
keep = np.array([...])
train_keep = train[np.isin(train, keep)]

Il s'agit probablement d'un algorithme O (M * N) plutôt que O (M) définit la recherche, mais si la vérification des éléments N dans keep est plus rapide qu'une recherche nominale O (1) , vous gagnez. p>

Vous pouvez obtenir quelque chose de plus proche de O (M log (N)) en utilisant la recherche triée:

train_keep = filter(lambda x: x in keeps, train)

Une meilleure alternative pourrait être pour ajouter np.inf ou un entier maximum hors limites au tableau keep trié, afin que vous n'ayez pas à faire la distinction entre les éléments manquants et les éléments de bord avec supplémentaire du tout. Quelque chose comme np.max (train.max () + 1, keep.max ()) ferait l'affaire:

keeps = set(keep)
train_keep = [k for k in train if k in keeps]

Pour les entrées aléatoires avec train.size = 10000

et keep.size = 10 , la méthode numpy est environ 10 fois plus rapide sur mon ordinateur portable.


10 commentaires

J'ai essayé votre solution intelligente en utilisant searchsorted () et c'est merveilleusement rapide, mais j'ai dû ajouter des parenthèses pour que les masques fonctionnent: mask = (ind> 0) & (ind et extra = (ind == 0) & (train == garder [0]) .


@John. J'apprécie la prise. Fixé.


Je pense que j'ai parlé trop tôt; Je n'ai pas réussi à faire fonctionner l'approche searchsorted () pour moi. Les documents triés par la recherche disent "Trouvez les index dans un tableau trié a tel que, si les éléments correspondants dans v étaient insérés avant les index, l'ordre de a serait conservé", ce qui n'est pas le même que correspondant éléments. Est-ce que je manque quelque chose? J'espérais aussi l'utiliser sur un tableau de caractères ...


... ou peut-être que cela fonctionnerait si train et keep étaient des index?


@John. Merci encore pour la capture. Apparemment, j'ai oublié une étape cruciale: vérifier que les éléments indexés sont bien égaux aux valeurs cibles. Je vais réparer correctement et tester quand j'arrive à un bureau, mais c'est quelque chose comme mask = (keep [ind] == train); result = train [masque]


@John. J'ai corrigé ma réponse. Je ne sais pas comment personne n'a remarqué cela avant ...


Impressionnant! Semble fonctionner parfaitement maintenant. BTW, je suis nouveau sur Numpy et mes données se composent de chaînes laides comme 'ABC_S8 # Q09 # 2 # 510a # 6'. J'ai lu quelque chose à propos de Numpy tronquant de longues chaînes de caractères. Dois-je m'inquiéter de l'échec du test d'égalité final ( train [keep [ind] == train] ) à cause d'une troncature? Aussi, je suppose que le grand nombre (99999) pourrait être juste len (data.index) + 1, non?


@John. Cela dépend vraiment. Si les chaînes signifient quelque chose, je recommanderais d'essayer de les transformer en chiffres. Sinon, vous devez vous assurer que le type de données est suffisamment long pour contenir la chaîne la plus longue, sinon, oui, certains pourraient être tronqués.


@John. Peut-être avez-vous un problème sur lequel poser une question? Je serais heureux de jeter un œil.


continuons cette discussion dans le chat .



0
votes

La logique est la même, mais essayez, peut-être qu'un générateur est plus rapide pour votre cas:

print(list(train_keep))

#  alternatively, uncomment this and comment out the line above,
#  it's because a generator can be consumed once
#  for e in train_keep:
#    print(e)

Enfin, convertissez-le en liste si nécessaire ou itérez directement le générateur: p>

def keep_if_in(to_keep, ary):
  for element in ary:
    if element in to_keep:
      yield element

train = [1, 2, 3, 4, 5, 5, 5, 5, 3, 2, 1]
keep = [1, 3, 4]
train_keep = keep_if_in(set(keep), train)


0 commentaires

0
votes

Il s'agit d'une légère extension de la technique intelligente de Mad Physicist, pour couvrir une situation où les listes contiennent des caractères et l'un d'eux est une colonne de dataframe (j'essayais de trouver une liste d'éléments dans une dataframe, y compris tous les doublons, mais la réponse évidente, mylist.isin (df ['col') a supprimé les doublons). J'ai adapté sa réponse pour traiter le problème d'une éventuelle troncature des données de caractères par Numpy.

#Sample dataframe with strings
d = {'train': ['ABC_S8#Q09#2#510a#6','ABC_S8#Q09#2#510l','ABC_S8#Q09#2#510a#6','ABC_S8#Q09#2#510d02','ABC_S8#Q09#2#510c#8y','ABC_S8#Q09#2#510a#6'], 'col2': [1,2,3,4,5,6]}
df = pd.DataFrame(data=d)

keep_list = ['ABC_S8#Q09#2#510a#6','ABC_S8#Q09#2#510b13','ABC_S8#Q09#2#510c#8y']

#Make sure the Numpy datatype accomodates longest string in either list
maxlen = max(len(max(keep_list, key = len)),len(max(df['train'], key = len))) 
strtype = '<U'+ str(maxlen) 

#Convert lists to Numpy arrays
keep = np.array(keep_list,dtype = strtype)
train = np.array(df['train'],dtype = strtype)

#Algorithm
keep.sort()
ind = np.searchsorted(keep, train, side='left')
ind[ind == keep.size] -= 1
train_keep = df[keep[ind] == df['train']] #reference the original dataframe

J'ai trouvé que c'était beaucoup plus rapide que les autres solutions que j'ai essayées.


2 commentaires

Il n'est pas pertinent qu'une liste soit un dataframe, car vous pouvez simplement faire df ['train']. Values ​​


Vous mentionnez que cela échoue pour des ensembles de données plus volumineux. Pouvez-vous générer un jeu de données aléatoire (avec une graine fixe) qui reproduit l'échec et poser une question à ce sujet?