python - Encuentra pares simétricos rápidamente en numpy




pandas (4)

frozenset

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

df[~mask]
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]

¿Cómo encuentro rápidamente identificar y eliminar el último duplicado de todos los pares simétricos en este marco de datos?

Por par simétrico quiero decir que (0, 1) es igual a (1, 0) . Este último debe ser eliminado.

Tiene que ser rápido, por lo que se aprecian soluciones vacías. No hay conversión a objetos python por favor :)


Aquí hay uno basado en NumPy para enteros:

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 desea mantener los datos del índice tal como están, use return df.iloc[np.sort(sidx[m])] .

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

# https://.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()

y simplemente reemplace el paso para obtener idx con idx = view1D(b) en remove_symm_pairs .


Si esto necesita ser rápido , y si sus variables son enteras, entonces el siguiente truco puede ayudar: sea v,w las columnas de su vector; construir [v+w, np.abs(vw)] =: [x, y] ; luego ordene esta matriz lexicográficamente, elimine los duplicados y finalmente vuelva a asignarla a [v, w] = [(x+y), (xy)]/2 .


Una forma es usar np.unique con return_index=True y usar el resultado para indexar el marco de datos:

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
...




numpy