Tengo un marco de datos con este tipo de datos:

      0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15   16   17   18   19   ...  309  310  311  312  313  314  315  316  317  318  319  320  321  322  323  324  325  326  327  328
0      18    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0  ...    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
1      84    1    0    0    0    0    0    0    0    0    0    0    0    0    0    0    1    0    0    0  ...    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
2      50    1    0    1    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0  ...    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0

La forma df es (10000, 329)

Me gustaría convertir el 5% aleatorio de 1 en el marco de datos a 0.

¿Es posible?

1
Ioan Kats 18 feb. 2020 a las 04:35

2 respuestas

La mejor respuesta

Prueba esto:

# Get all columns from 1 to 328 and stack them into a temp series
tmp = df.loc[:, 1:].stack()

# Get the 1s
ones = tmp[tmp == 1].values.astype('int8')

# Mix with 5% zeros. You can use ceil or floor here
# as long as it makes an integer
n_zero = np.ceil(ones.shape[0] * .05).astype('int')

# Make the 0s
zeros = np.zeros(n_zero, dtype='int8')

# Replace 5% of the 1s with 0s and shuffle them
noise = np.concatenate((ones[n_zero:], zeros))
np.random.shuffle(noise)

# Assign the noise back to `tmp`
tmp.loc[tmp == 1] = noise

# Assign the noise back to the orignal frame
df.loc[:, 1:] = tmp.unstack()

Puede saber si el 5% de 1s ha sido reemplazado por 0s sumando los cuadros anterior y posterior:

# Run this before and after the last line above to verify
df.loc[:, 1:].values.sum()
1
Code Different 18 feb. 2020 a las 03:16

Aquí hay una solución más larga en la que imprimo los distintos pasos.

Cree el conjunto de datos de muestra con numpy. Las dimensiones y los valores se modificarán de la pregunta para que la respuesta sea más clara. rawmat será una matriz de ceros y unos de 10 por 10, excepto la primera columna, que son valores más grandes. Entre los ceros y unos hay un 50 por ciento de probabilidad de que se obtenga uno.

import numpy as np
np.random.seed(1000)
rawmat = np.random.randint(2,size=(10,10))
# insert higher values in the first column
rawmat[:,0] = np.random.randint(low=5,high=9,size=10)
print(rawmat)

[[5 1 1 0 1 0 0 1 1 0]
[6 1 0 1 0 1 0 0 1 1]
[5 0 1 0 0 0 0 1 0 0]
[6 0 0 0 1 0 0 1 1 0]
[6 0 1 1 0 1 0 1 0 0]
[5 1 0 0 1 0 0 1 0 1]
[5 1 1 0 1 0 1 0 1 1]
[5 1 1 1 1 1 1 0 1 1]
[7 1 1 1 0 0 0 0 1 1]
[8 0 0 0 1 1 0 1 1 0]]

De 100 celdas, 90 son ahora cero o uno. De hecho, 46 son 1, lo cual es razonable dada la probabilidad del 50 por ciento.

np.count_nonzero(rawmat==1)

46
Podemos crear una máscara donde el 50 por ciento de las observaciones relevantes son verdaderas con randmask. Sin embargo, el truco en estas preguntas es centrarse solo en las preguntas, por lo que obtenemos esto con rawones.

randmask = np.random.choice(a=[False, True], size=(10,10),p=[0.5,0.5])
rawones = np.where(rawmat==1,rawmat,0)
onefin = np.where(randmask,onemask,np.zeros((10,10),dtype=int))

Ahora la cantidad de unidades disminuirá a la mitad. Inicialmente había 46 unos en rawmat y ahora hay 23 en onefine.

np.count_nonzero(onefin==1)

23
Los filtrados se pueden recombinar con los datos antiguos para obtener una matriz con la mitad de los.

finmat = np.where(rawmat==1,onefin,rawmat)
print(finmat)

[[5 0 0 0 0 0 0 1 1 0]
[6 1 0 1 0 0 0 0 1 0]
[5 0 1 0 0 0 0 1 0 0]
[6 0 0 0 0 0 0 1 0 0]
[6 0 0 1 0 0 0 0 0 0]
[5 1 0 0 1 0 0 0 0 1]
[5 0 1 0 1 0 1 0 0 0]
[5 1 0 0 1 1 0 0 1 1]
[7 1 1 0 0 0 0 0 0 0]
[8 0 0 0 0 1 0 0 0 0]]
Ahora tenemos la matriz original con el número de unidades reducidas a la mitad de 46 a 23.

np.count_nonzero(finmat==1)

23

0
Harold Henson 18 feb. 2020 a las 15:58