Trouvez rapidement des paires symétriques dans numpy

15
from itertools import product
import pandas as pd

df = pd.DataFrame.from_records(product(range(10), range(10)))
df = df.sample(90)
df.columns = "c1 c2".split()
df = df.sort_values(df.columns.tolist()).reset_index(drop=True)
#     c1  c2
# 0    0   0
# 1    0   1
# 2    0   2
# 3    0   3
# 4    0   4
# ..  ..  ..
# 85   9   4
# 86   9   5
# 87   9   7
# 88   9   8
# 89   9   9
# 
# [90 rows x 2 columns]

Comment trouver, identifier et supprimer rapidement le dernier doublon de toutes les paires symétriques de cette trame de données?

Un exemple de paire symétrique est que «(0, 1)» est égal à «(1, 0)». Ce dernier doit être supprimé.

L'algorithme doit être rapide, il est donc recommandé d'utiliser numpy. La conversion en objet python n'est pas autorisée.

Le chat unfun
la source
1
Pourriez-vous donner un exemple de ce que vous comprenez symmetric pairs?
yatu
(0, 1) == (1,0) est vrai
The Unfun Cat
1
Est-ce que (0, 1) == (0, 1) est également vrai?
wundermahn
@JerryM. Oui, mais il est trivial d'enlever avecdf.drop_duplicates()
The Unfun Cat
2
@ molybdenum42 J'utilise le produit itertools pour créer un exemple, les données elles-mêmes ne sont pas créées avec le produit itertools.
The Unfun Cat

Réponses:

13

Vous pouvez trier les valeurs, puis groupby:

a= np.sort(df.to_numpy(), axis=1)
df.groupby([a[:,0], a[:,1]], as_index=False, sort=False).first()

Option 2 : Si vous avez beaucoup de paires c1, c2, cela groupbypeut être lent. Dans ce cas, nous pouvons attribuer de nouvelles valeurs et filtrer par drop_duplicates:

a= np.sort(df.to_numpy(), axis=1) 

(df.assign(one=a[:,0], two=a[:,1])   # one and two can be changed
   .drop_duplicates(['one','two'])   # taken from above
   .reindex(df.columns, axis=1)
)
Quang Hoang
la source
7

Une façon consiste à utiliser np.uniqueavec return_index=Trueet à utiliser le résultat pour indexer la trame de données:

a = np.sort(df.values)
_, ix = np.unique(a, return_index=True, axis=0)

print(df.iloc[ix, :])

    c1  c2
0    0   0
1    0   1
20   2   0
3    0   3
40   4   0
50   5   0
6    0   6
70   7   0
8    0   8
9    0   9
11   1   1
21   2   1
13   1   3
41   4   1
51   5   1
16   1   6
71   7   1
...
yatu
la source
1
Oui sinon échoue unique de détecter des paires symétriques
YATU
Ok, je vois donc vous
triez
Oui mais je veux dire que vous transformez [1, 0] en [0, 1] non?
Dani Mesejo
6

frozenset

mask = pd.Series(map(frozenset, zip(df.c1, df.c2))).duplicated()

df[~mask]
piRSquared
la source
1
N'êtes-vous pas en train d'itérer lentement sur des tuples sur chaque colonne ici? Pourtant, vote positif.
The Unfun Cat
Oui, je répète. Non, ce n'est pas aussi lent que vous le pensez.
piRSquared
5

je ferai

df[~pd.DataFrame(np.sort(df.values,1)).duplicated().values]

De pandas et numpy tri

s=pd.crosstab(df.c1,df.c2)
s=s.mask(np.triu(np.ones(s.shape)).astype(np.bool) & s==0).stack().reset_index()
YOBEN_S
la source
5

En voici un basé sur NumPy pour les entiers -

def remove_symm_pairs(df):
    a = df.to_numpy(copy=False)
    b = np.sort(a,axis=1)
    idx = np.ravel_multi_index(b.T,(b.max(0)+1))
    sidx = idx.argsort(kind='mergesort')
    p = idx[sidx]
    m = np.r_[True,p[:-1]!=p[1:]]
    a_out = a[np.sort(sidx[m])]
    df_out = pd.DataFrame(a_out)
    return df_out

Si vous souhaitez conserver les données d'index telles quelles, utilisez return df.iloc[np.sort(sidx[m])].

Pour les nombres génériques (ints / float, etc.), nous utiliserons un view-based-

# https://stackoverflow.com/a/44999009/ @Divakar
def view1D(a): # a is array
    a = np.ascontiguousarray(a)
    void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
    return a.view(void_dt).ravel()

et remplacer simplement l'étape pour obtenir idxavec idx = view1D(b)en remove_symm_pairs.

Divakar
la source
1

Si cela doit être rapide , et si vos variables sont entières, alors l'astuce suivante peut vous aider: v,wsoit les colonnes de votre vecteur; construire [v+w, np.abs(v-w)] =: [x, y]; puis triez cette matrice lexicographiquement, supprimez les doublons et enfin mappez-la à nouveau [v, w] = [(x+y), (x-y)]/2.

Federico Poloni
la source