Encontre pares simétricos rapidamente em 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]

Como localizo, identifico e removo rapidamente a última duplicata de todos os pares simétricos nesse quadro de dados?

Um exemplo de par simétrico é que '(0, 1)' é igual a '(1, 0)'. Este último deve ser removido.

O algoritmo deve ser rápido, por isso é recomendável usar numpy. A conversão para objeto python não é permitida.

The Unfun Cat
fonte
11
Você poderia dar um exemplo do que entende symmetric pairs?
yatu 28/10/19
(0, 1) == (1,0) é verdadeiro
The Unfun Cat
11
É (0, 1) == (0, 1) também verdadeiro?
Wundermahn 28/10/19
@JerryM. Sim, mas é trivial remover com #df.drop_duplicates()
The Unfun Cat 28/10/19
2
@ molybdenum42 Eu uso o produto itertools para criar um exemplo, os dados em si não são criados com o produto itertools.
The Unfun Cat

Respostas:

13

Você pode classificar os valores e, em seguida groupby:

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

Opção 2 : se você tiver muitos pares c1, c2, groupbypode ser lento. Nesse caso, podemos atribuir novos valores e filtrar por 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
fonte
7

Uma maneira é usar np.uniquecom return_index=Truee usar o resultado para indexar o quadro de dados:

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
fonte
11
Sim, caso contrário, o único falha em detectar pares simétricos @DanielMesejo
yatu #
Ok, entendo, você está classificando os pares #
Dani Mesejo 28/10/19
Sim, mas quero dizer que você transforma [1, 0] em [0, 1], certo?
21819 Dani Mesejo
6

frozenset

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

df[~mask]
piRSquared
fonte
11
Você não está iterando lentamente sobre as tuplas em cada coluna aqui? Ainda, voto positivo.
The Unfun Cat
Sim, estou iterando. Não, não é tão lento quanto você pensa.
piRSquared
5

eu farei

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

De pandas e tri numpy

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
fonte
5

Aqui está um baseado em NumPy para números inteiros -

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

Se você deseja manter os dados do índice como estão, use return df.iloc[np.sort(sidx[m])].

Para números genéricos (ints / floats, etc.), usaremos view-basedum -

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

e simplesmente substituir o passo para chegar idxcom idx = view1D(b)no remove_symm_pairs.

Divakar
fonte
1

Se isso precisar ser rápido , e se suas variáveis ​​forem inteiras, o seguinte truque pode ajudar: v,wsejam as colunas do seu vetor; construir [v+w, np.abs(v-w)] =: [x, y]; em seguida, classifique essa matriz lexicograficamente, remova duplicatas e, finalmente, mapeie-a novamente [v, w] = [(x+y), (x-y)]/2.

Federico Poloni
fonte