He mirado alrededor (p. Ej., aquí ), pero no puedo entender por qué mi código no funciona como se esperaba. Tengo un marco de datos de pandas y me gustaría agregar una columna que marque el último elemento cero en la columna B sobre un elemento distinto de cero.

df = pd.DataFrame({'B':[0,0,1,0,1,0,0,1]})
N = len(df.index)
df['C'] = N*[False]
for i in range(N-1):
    if (df.iloc[i]['B']==0 and df.iloc[i+1]['B']>0):
        df.iloc[i]['C']=True

A pesar de tener la condición satisfecha 3 veces, la columna C sigue siendo falsa, y también recibo una advertencia que no entiendo:

SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame

¿Alguna idea?

0
Nonancourt 23 jun. 2020 a las 20:43

3 respuestas

La mejor respuesta

Para los marcos de datos con tipos mixtos (como aquí), parece que los pandas crean copias cuando se usan iloc y funciones similares. En lugar de indexar en cadena, puede hacer esto:

df.iloc[i, df.columns.get_loc('C')]=True

O

df.at[i, 'C'] = True

Sin embargo, sugeriría reemplazar su bucle for con esto, que me parece mucho más simple:

df['C'] = [df.iloc[i]['B'] == 0 and df.iloc[i+1]['B'] > 0 for i in range(N - 1)] + [False]

Editar: si realmente desea encontrar la última aparición de un elemento distinto de cero antes de un elemento que es cero, intente esto:

df['C'].where(df['C']).last_valid_index()

Esto genera 6

1
user 23 jun. 2020 a las 18:11

Ordenar por índice descendente y luego recorrer para encontrar la primera fila.

df=df.sort_index(ascending=False)
df['C'] = False
for i in range(len(df['B'])):
    if df.iloc[i-1,0] - 1 == df.iloc[i,0]:
        df.iloc[i,1] = True
        break
df=df.sort_index(ascending=True)
df

    B   C
0   0   False
1   0   False
2   1   False
3   0   False
4   1   False
5   0   False
6   0   True
7   1   False
0
David Erickson 23 jun. 2020 a las 17:55

Puede cambiar df.iloc[i]['C']=True desde su bucle for a df.loc[i, 'C'] = True para que funcione.

Pero preferiría usar lo siguiente para hacerlo un poco más eficiente:

df = pd.DataFrame({'B':[0,0,1,0,1,0,0,1]})

df['Check'] = df['B'].shift(-1)
df['C'] = df['B'] < df['Check']

Out:
   B  Check      C
0  0    0.0  False
1  0    1.0   True
2  1    0.0  False
3  0    1.0   True
4  1    0.0  False
5  0    0.0  False
6  0    1.0   True
7  1    NaN  False
0
frank 23 jun. 2020 a las 17:58