Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

438
Vistas
Pandas: cambio condicional

¿Hay alguna manera de cambiar una columna de marco de datos dependiendo de la condición en otras dos columnas? algo como:

 df["cumulated_closed_value"] = df.groupby("user").['close_cumsum'].shiftWhile(df['close_time']>df['open_time])

He descubierto una manera de hacer esto, pero es ineficiente:

1) Cargue datos y cree la columna para cambiar

 df=pd.read_csv('data.csv') df.sort_values(['user','close_time'],inplace=True) df['close_cumsum']=df.groupby('user')['value'].cumsum() df.sort_values(['user','open_time'],inplace=True) print(df)

producción:

 user open_time close_time value close_cumsum 0 1 2017-01-01 2017-03-01 5 18 1 1 2017-01-02 2017-02-01 6 6 2 1 2017-02-03 2017-02-05 7 13 3 1 2017-02-07 2017-04-01 3 21 4 1 2017-09-07 2017-09-11 1 22 5 2 2018-01-01 2018-02-01 15 15 6 2 2018-03-01 2018-04-01 3 18

2) cambiar la columna con una autounión y algunos filtros

Unión automática (esto es memoria ineficiente) df2=pd.merge(df[['user','open_time']],df[['user','close_time','close_cumsum']], on='user')

filtrar por 'hora_de_cierre' < 'hora_de_apertura'. Luego obtenga la fila con el tiempo de cierre máximo

 df2=df2[df2['close_time']<df2['open_time']] idx = df2.groupby(['user','open_time'])['close_time'].transform(max) == df2['close_time'] df2=df2[idx]

3) fusionar con el conjunto de datos original:

 df3=pd.merge(df[['user','open_time','close_time','value']],df2[['user','open_time','close_cumsum']],how='left') print(df3)

producción:

 user open_time close_time value close_cumsum 0 1 2017-01-01 2017-03-01 5 NaN 1 1 2017-01-02 2017-02-01 6 NaN 2 1 2017-02-03 2017-02-05 7 6.0 3 1 2017-02-07 2017-04-01 3 13.0 4 1 2017-09-07 2017-09-11 1 21.0 5 2 2018-01-01 2018-02-01 15 NaN 6 2 2018-03-01 2018-04-01 3 15.0

¿Hay una forma más pandas de obtener el mismo resultado?

Editar: he agregado una línea de datos para que el caso sea más claro. Mi objetivo es obtener la suma de todas las transacciones cerradas antes de la hora de apertura de la nueva transacción

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Estoy usando un nuevo párrafo aquí para registrar la condición df2['close_time']<df2['open_time']

 df['New']=((df.open_time-df.close_time.shift()).dt.days>0).shift(-1) s=df.groupby('user').apply(lambda x : (x['value']*x['New']).cumsum().shift()).reset_index(level=0,drop=True) s.loc[~(df.New.shift()==True)]=np.nan df['Cumsum']=s df Out[1043]: user open_time close_time value New Cumsum 0 1 2017-01-01 2017-03-01 5 False NaN 1 1 2017-01-02 2017-02-01 6 True NaN 2 1 2017-02-03 2017-02-05 7 True 6 3 1 2017-02-07 2017-04-01 3 False 13 4 2 2017-01-01 2017-02-01 15 True NaN 5 2 2017-03-01 2017-04-01 3 NaN 15

Actualización: desde op actualice la pregunta (Datos de Gabriel A)

 df['New']=df.user.map(df.groupby('user').close_time.apply(lambda x: np.array(x))) df['New1']=df.user.map(df.groupby('user').value.apply(lambda x: np.array(x))) df['New2']=[[x>m for m in y] for x,y in zip(df['open_time'],df['New']) ] df['Yourtarget']=list(map(sum,df['New2']*df['New1'].values)) df.drop(['New','New1','New2'],1) Out[1376]: user open_time close_time value Yourtarget 0 1 2016-12-30 2016-12-31 1 0 1 1 2017-01-01 2017-03-01 5 1 2 1 2017-01-02 2017-02-01 6 1 3 1 2017-02-03 2017-02-05 7 7 4 1 2017-02-07 2017-04-01 3 14 5 1 2017-09-07 2017-09-11 1 22 6 2 2018-01-01 2018-02-01 15 0 7 2 2018-03-01 2018-04-01 3 15
over 4 years ago · Santiago Trujillo Denunciar

0

Hice una modificación a su caso de prueba que creo que debería incluir. Esta solución maneja su edición.

 import pandas as pd import numpy as np df = pd.read_csv("cond_shift.csv") df

aporte:

 user open_time close_time value 0 1 12/30/2016 12/31/2016 1 1 1 1/1/2017 3/1/2017 5 2 1 1/2/2017 2/1/2017 6 3 1 2/3/2017 2/5/2017 7 4 1 2/7/2017 4/1/2017 3 5 1 9/7/2017 9/11/2017 1 6 2 1/1/2018 2/1/2018 15 7 2 3/1/2018 4/1/2018 3

crear columnas para cambiar:

 df["open_time"] = pd.to_datetime(df["open_time"]) df["close_time"] = pd.to_datetime(df["close_time"]) df.sort_values(['user','close_time'],inplace=True) df['close_cumsum']=df.groupby('user')['value'].cumsum() df.sort_values(['user','open_time'],inplace=True) df user open_time close_time value close_cumsum 0 1 2016-12-30 2016-12-31 1 1 1 1 2017-01-01 2017-03-01 5 19 2 1 2017-01-02 2017-02-01 6 7 3 1 2017-02-03 2017-02-05 7 14 4 1 2017-02-07 2017-04-01 3 22 5 1 2017-09-07 2017-09-11 1 23 6 2 2018-01-01 2018-02-01 15 15 7 2 2018-03-01 2018-04-01 3 18

Columnas de cambio (explicación a continuación):

 df["cumulated_closed_value"] = df.groupby("user")["close_cumsum"].transform("shift") condition = ~(df.groupby("user")['close_time'].transform("shift") < df["open_time"]) df.loc[ condition,"cumulated_closed_value" ] = None df["cumulated_closed_value"] =df.groupby("user")["cumulated_closed_value"].fillna(method="ffill").fillna(0) df user open_time close_time value close_cumsum cumulated_closed_value 0 1 2016-12-30 2016-12-31 1 1 0.0 1 1 2017-01-01 2017-03-01 5 19 1.0 2 1 2017-01-02 2017-02-01 6 7 1.0 3 1 2017-02-03 2017-02-05 7 14 7.0 4 1 2017-02-07 2017-04-01 3 22 14.0 5 1 2017-09-07 2017-09-11 1 23 22.0 6 2 2018-01-01 2018-02-01 15 15 0.0 7 2 2018-03-01 2018-04-01 3 18 15.0

Todo esto se ha escrito de tal manera que se hace en todos los usuarios. Creo que la lógica es más fácil si solo te enfocas en un usuario a la vez.

  • Suponga que ningún evento ocurre al mismo tiempo. Esto es lo mismo que desplazar la suma acumulada una fila hacia abajo.
  • Elimina los eventos que suceden al mismo tiempo que otros eventos.
  • Completa los valores que faltan. Con relleno hacia delante.

Todavía probaría esto a fondo antes de usarlo. Los intervalos de tiempo son extraños y hay muchos casos extremos.

over 4 years ago · Santiago Trujillo Denunciar

0

(Nota: la respuesta de @wen me parece bien, así que no estoy seguro de si el OP está buscando algo más o algo diferente. En cualquier caso, aquí hay un enfoque alternativo que usa merge_asof que también debería funcionar bien).

Primero remodele los marcos de datos de la siguiente manera:

 lookup = ( df[['close_time','value','user']].set_index(['user','close_time']) .sort_index().groupby('user').cumsum().reset_index(0) ) df = df.set_index('open_time').sort_index()

La idea con "búsqueda" es simplemente ordenar por "hora_de_cierre" y luego tomar una suma acumulativa (agrupada):

 user value close_time 2017-02-01 1 6 2017-02-05 1 13 2017-03-01 1 18 2017-04-01 1 21 2017-09-11 1 22 2018-02-01 2 15 2018-04-01 2 18

Para "df" simplemente tomamos un subconjunto del marco de datos original:

 user close_time value open_time 2017-01-01 1 2017-03-01 5 2017-01-02 1 2017-02-01 6 2017-02-03 1 2017-02-05 7 2017-02-07 1 2017-04-01 3 2017-09-07 1 2017-09-11 1 2018-01-01 2 2018-02-01 15 2018-03-01 2 2018-04-01 3

A partir de aquí, solo desea fusionar conceptualmente los dos conjuntos de datos en "usuario" y "tiempo_abierto"/"tiempo_cerrado", pero el factor complicado es que no queremos hacer una coincidencia exacta en el tiempo, sino más bien una especie de coincidencia "más cercana".

Para estos tipos de fusiones, puede usar merge_asof que es una gran herramienta para varias coincidencias no exactas (incluidas 'más cercana', 'hacia atrás' y 'hacia adelante'). Desafortunadamente, debido a la inclusión de groupby, también es necesario recorrer a los usuarios, pero sigue siendo un código bastante simple de leer:

 df_merged = pd.DataFrame() for u in df['user'].unique(): df_merged = df_merged.append( pd.merge_asof( df[df.user==u], lookup[lookup.user==u], left_index=True, right_index=True, direction='backward' ) ) df_merged.drop('user_y',axis=1).rename({'value_y':'close_cumsum'},axis=1)

Resultados:

 user_x close_time value_x close_cumsum open_time 2017-01-01 1 2017-03-01 5 NaN 2017-01-02 1 2017-02-01 6 NaN 2017-02-03 1 2017-02-05 7 6.0 2017-02-07 1 2017-04-01 3 13.0 2017-09-07 1 2017-09-11 1 21.0 2018-01-01 2 2018-02-01 15 NaN 2018-03-01 2 2018-04-01 3 15.0
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda