tablas seleccionar para manejo libreria leer funciones eliminar duplicados datos data columnas python pandas numpy dataframe duplicates

python - seleccionar - Agrupe los ID de columna duplicados en el marco de datos de pandas



seleccionar columnas en pandas (7)

Aquí hay un enfoque NumPy -

def group_duplicate_cols(df): a = df.values sidx = np.lexsort(a) b = a[:,sidx] m = np.concatenate(([False], (b[:,1:] == b[:,:-1]).all(0), [False] )) idx = np.flatnonzero(m[1:] != m[:-1]) C = df.columns[sidx].tolist() return [C[i:j] for i,j in zip(idx[::2],idx[1::2]+1)]

Ejecuciones de muestra

In [100]: df Out[100]: A B C D E F a1 1 2 1 2 3 1 a2 2 4 2 4 4 1 a3 3 2 3 2 2 1 a4 4 1 4 1 1 1 a5 5 9 5 9 2 1 In [101]: group_duplicate_cols(df) Out[101]: [[''A'', ''C''], [''B'', ''D'']] # Let''s add one more duplicate into group containing ''A'' In [102]: df.F = df.A In [103]: group_duplicate_cols(df) Out[103]: [[''A'', ''C'', ''F''], [''B'', ''D'']]

Convertir para hacer lo mismo, pero para las filas (índice), solo necesitamos cambiar las operaciones a lo largo del otro eje, así -

def group_duplicate_rows(df): a = df.values sidx = np.lexsort(a.T) b = a[sidx] m = np.concatenate(([False], (b[1:] == b[:-1]).all(1), [False] )) idx = np.flatnonzero(m[1:] != m[:-1]) C = df.index[sidx].tolist() return [C[i:j] for i,j in zip(idx[::2],idx[1::2]+1)]

Ejecución de la muestra

In [260]: df2 Out[260]: a1 a2 a3 a4 a5 A 3 5 3 4 5 B 1 1 1 1 1 C 3 5 3 4 5 D 2 9 2 1 9 E 2 2 2 1 2 F 1 1 1 1 1 In [261]: group_duplicate_rows(df2) Out[261]: [[''B'', ''F''], [''A'', ''C'']]

Benchmarking

Enfoques -

# @John Galt''s soln-1 from itertools import combinations def combinations_app(df): return[x for x in combinations(df.columns, 2) if (df[x[0]] == df[x[-1]]).all()] # @Abdou''s soln def pandas_groupby_app(df): return [tuple(d.index) for _,d in df.T.groupby(list(df.T.columns)) if len(d) > 1] # @COLDSPEED''s soln def triu_app(df): c = df.columns.tolist() i, j = np.triu_indices(len(c), 1) x = [(c[_i], c[_j]) for _i, _j in zip(i, j) if (df[c[_i]] == df[c[_j]]).all()] return x # @cmaher''s soln def lambda_set_app(df): return list(filter(lambda x: len(x) > 1, list(set([tuple([x for x in df.columns if all(df[x] == df[y])]) for y in df.columns]))))

Nota: la @John Galt''s soln-2 no se incluyó porque las entradas de tamaño (8000,500) explotarían con la broadcasting propuesta para esa.

Tiempos -

In [179]: # Setup inputs with sizes as mentioned in the question ...: df = pd.DataFrame(np.random.randint(0,10,(8000,500))) ...: df.columns = [''C''+str(i) for i in range(df.shape[1])] ...: idx0 = np.random.choice(df.shape[1], df.shape[1]//2,replace=0) ...: idx1 = np.random.choice(df.shape[1], df.shape[1]//2,replace=0) ...: df.iloc[:,idx0] = df.iloc[:,idx1].values ...: # @John Galt''s soln-1 In [180]: %timeit combinations_app(df) 1 loops, best of 3: 24.6 s per loop # @Abdou''s soln In [181]: %timeit pandas_groupby_app(df) 1 loops, best of 3: 3.81 s per loop # @COLDSPEED''s soln In [182]: %timeit triu_app(df) 1 loops, best of 3: 25.5 s per loop # @cmaher''s soln In [183]: %timeit lambda_set_app(df) 1 loops, best of 3: 27.1 s per loop # Proposed in this post In [184]: %timeit group_duplicate_cols(df) 10 loops, best of 3: 188 ms per loop

Super boost con la funcionalidad de visualización de NumPy.

Aprovechando la funcionalidad de vista de NumPy que nos permite ver cada grupo de elementos como un solo tipo de dato, podríamos obtener un aumento de rendimiento notable, como así:

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() def group_duplicate_cols_v2(df): a = df.values sidx = view1D(a.T).argsort() b = a[:,sidx] m = np.concatenate(([False], (b[:,1:] == b[:,:-1]).all(0), [False] )) idx = np.flatnonzero(m[1:] != m[:-1]) C = df.columns[sidx].tolist() return [C[i:j] for i,j in zip(idx[::2],idx[1::2]+1)]

Tiempos -

In [322]: %timeit group_duplicate_cols(df) 10 loops, best of 3: 185 ms per loop In [323]: %timeit group_duplicate_cols_v2(df) 10 loops, best of 3: 69.3 ms per loop

Solo locuras aceleraciones!

Ahora hay muchas preguntas similares, pero la mayoría responde cómo eliminar las columnas duplicadas. Sin embargo, quiero saber cómo puedo hacer una lista de tuplas donde cada tupla contiene los nombres de columna de columnas duplicadas. Supongo que cada columna tiene un nombre único. Solo para ilustrar mi pregunta:

df = pd.DataFrame({''A'': [1, 2, 3, 4, 5],''B'': [2, 4, 2, 1, 9], ''C'': [1, 2, 3, 4, 5],''D'': [2, 4, 2, 1, 9], ''E'': [3, 4, 2, 1, 2],''F'': [1, 1, 1, 1, 1]}, index = [''a1'', ''a2'', ''a3'', ''a4'', ''a5''])

Entonces quiero la salida:

[(''A'', ''C''), (''B'', ''D'')]

Y si se siente bien hoy, entonces también extienda la misma pregunta a las filas. Cómo obtener una lista de tuplas donde cada tupla contiene filas duplicadas.


Aquí hay una opción más que usa solo comprensiones / incorporaciones:

filter(lambda x: len(x) > 1, list(set([tuple([x for x in df.columns if all(df[x] == df[y])]) for y in df.columns])))

Resultado:

[(''A'', ''C''), (''B'', ''D'')]


Aquí hay una sola línea

In [22]: from itertools import combinations In [23]: [x for x in combinations(df.columns, 2) if (df[x[0]] == df[x[-1]]).all()] Out[23]: [(''A'', ''C''), (''B'', ''D'')]

Alternativamente, utilizando la transmisión NumPy. Mejor, mira la solution de Divakar.

In [124]: cols = df.columns In [125]: dftv = df.T.values In [126]: cross = pd.DataFrame((dftv == dftv[:, None]).all(-1), cols, cols) In [127]: cross Out[127]: A B C D E F A True False True False False False B False True False True False False C True False True False False False D False True False True False False E False False False False True False F False False False False False True # Only take values from lower triangle In [128]: s = cross.where(np.tri(*cross.shape, k=-1)).unstack() In [129]: s[s == 1].index.tolist() Out[129]: [(''A'', ''C''), (''B'', ''D'')]


Basado en @John Galt one liner que es así:

result_col = [x for x in combinations(df.columns, 2) if (df[x[0]] == df[x[-1]]).all()]

puede obtener el result_row siguiente manera:

result_row = [x for x in combinations(df.T.columns,2) if (df.T[x[0]] == df.T[x[-1]]).all()]

utilizando transpose (df.T)


Este es otro enfoque que utiliza Python puro:

from operator import itemgetter from itertools import groupby def myfunc(df): # Convert the dataframe to a list of list including the column name zipped = zip(df.columns, df.values.T.tolist()) # Sort the columns (so they can be grouped) zipped_sorted = sorted(zipped, key=itemgetter(1)) # Placeholder for the result res = [] res_append = res.append # Find duplicated columns using itertools.groupby for k, grp in groupby(zipped_sorted, itemgetter(1)): grp = list(grp) if len(grp) > 1: res_append(tuple(map(itemgetter(0), grp))) return res

Incluí algunos comentarios en línea que ilustran cómo funciona, pero básicamente esto solo ordena la entrada para que las columnas idénticas sean adyacentes y luego las agrupe.

Hice algunos tiempos superficiales utilizando la configuración de tiempos de Divakars y obtuve lo siguiente:

%timeit group_duplicate_cols(df) 391 ms ± 25.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit myfunc(df) 572 ms ± 4.36 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Por lo tanto, parece que solo es 2 veces más lento que un enfoque NumPy, lo que en realidad es increíble.


Esto también debería hacer:

[tuple(d.index) for _,d in df.T.groupby(list(df.T.columns)) if len(d) > 1]

Rendimientos:

# [(''A'', ''C''), (''B'', ''D'')]


No usando panda, solo puro pitón:

data = {''A'': [1, 2, 3, 4, 5],''B'': [2, 4, 2, 1, 9], ''C'': [1, 2, 3, 4, 5],''D'': [2, 4, 2, 1, 9], ''E'': [3, 4, 2, 1, 2],''F'': [1, 1, 1, 1, 1]} from collections import defaultdict deduplicate = defaultdict(list) for key, items in data.items(): deduplicate[tuple(items)].append(key) # cast to tuple because they are hashables but lists are not. duplicates = list() for vector, letters in deduplicate.items(): if len(letters) > 1: duplicates.append(letters) print(duplicates)

Utilizando pandas:

import pandas df = pandas.DataFrame(data) duplicates = [] dedup2 = defaultdict(list) for key in df.columns: dedup2[tuple(df[key])].append(key) duplicates = list() for vector, letters in dedup2.items(): if len(letters) > 1: duplicates.append(letters) print(duplicates)

No es realmente agradable, pero puede ser más rápido ya que todo se hace en una iteración sobre los datos.

dedup2 = defaultdict(list) duplicates = {} for key in df.columns: astup = tuple(df[key]) duplic = dedup2[astup] duplic.append(key) if len(duplic) > 1: duplicates[astup] = duplic duplicates = duplicates.values() print(duplicates)