Intención: filtrar números binarios basados en pesos de hamming usando pandas. Aquí verifico el número de 1s que ocurren en el binario y escribo el conteo en df.

Esfuerzo hasta ahora:

import pandas as pd
def ones(num):
    return bin(num).count('1')
num = list(range(1,8))
C = pd.Index(["num"])
df = pd.DataFrame(num, columns=C)
df['count'] = df.apply(lambda row : ones(row['num']), axis = 1)
print(df) 

Salida:

   num  count
0    1      1
1    2      1
2    3      2
3    4      1
4    5      2
5    6      2
6    7      3


Intended output:
  1 2 3
0 1 3 7
1 2 5
2 4 6

¡Ayuda!

0
Programmer_nltk 26 jun. 2020 a las 16:50

3 respuestas

La mejor respuesta

Puede usar pivot_table. Aunque deberá definir index como cumcount de la columna agrupada count, pivot_table no puede resolverlo por sí solo :)

(df.pivot_table(index=df.groupby('count').cumcount(), 
                columns='count', 
                values='num'))

count    1    2    3
0      1.0  3.0  7.0
1      2.0  5.0  NaN
2      4.0  6.0  NaN

También tiene el parámetro fill_value, aunque no le recomendaría que lo use, ya que obtendrá tipos mixtos. Ahora parece que NumPy sería una buena opción desde aquí, puede obtener fácilmente una matriz del resultado con new_df.to_numpy().


Además, centrándonos en la lógica en ones, podemos vectorizar esto con (basado en esta respuesta):

m = df.num.to_numpy().itemsize
df['count'] = (df.num.to_numpy()[:,None] & (1 << np.arange(m)) > 0).view('i1').sum(1)

Aquí hay una verificación del rendimiento de ambos enfoques:

df_large = pd.DataFrame({'num':np.random.randint(0,10,(10_000))})

def vect(df):
    m = df.num.to_numpy().itemsize
    (df.num.to_numpy()[:,None] & (1 << np.arange(m)) > 0).view('i1').sum(1)

%timeit vect(df_large)
# 340 µs ± 5.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df_large.apply(lambda row : ones(row['num']), axis = 1)
# 103 ms ± 2.32 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
3
yatu 26 jun. 2020 a las 14:32

Sugiero una salida diferente:

df.groupby("count").agg(list)

Que te dará

             num
count           
1      [1, 2, 4]
2      [3, 5, 6]
3            [7]

Es la misma información en un formato ligeramente diferente. En su formato pivote original, las filas no tienen sentido y tiene un número indeterminado de columnas. Sugiero que es más común tener un número indeterminado de filas. Creo que será más fácil trabajar con esto en el futuro.

O considere simplemente crear un diccionario ya que DataFrame está agregando una gran cantidad de gastos generales aquí sin ningún beneficio:

df.groupby("count").agg(list).to_dict()["num"]

Que te da

{
    1: [1, 2, 4], 
    2: [3, 5, 6], 
    3: [7],
}
1
Dan 26 jun. 2020 a las 14:01

Aquí hay un enfoque

df.groupby('count')['num'].agg(list).apply(pd.Series).T
0
Mark Wang 26 jun. 2020 a las 15:21