python - Obtenga el valor máximo comparando varias columnas y devuelva valores específicos
python-3.x pandas (8)
Sin usar hechicería
numpy
:
- Primero, hay algunas soluciones realmente buenas para este problema, por otros.
-
Los datos serán los proporcionados en la pregunta, como
df
# find the max value in the Duration columns
max_value = max(df.filter(like=''Dur'', axis=1).max().tolist())
# get a Boolean match of the dataframe for max_value
df_max = df[df == mv]
# get the row index
max_index = df_max.dropna(how=''all'').index[0]
# get the column name
max_col = df_max.dropna(axis=1, how=''all'').columns[0]
# get column index
max_col_index = df.columns.get_loc(max_col)
# final
df.iloc[max_index, [0, max_col_index, max_col_index + 1]]
Salida:
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Actualizar
-
Anoche, en realidad a las 4 am, descarté una mejor solución, porque estaba demasiado cansada.
-
max_value = max(df.filter(like=''Dur'', axis=1).max().tolist())
, para devolver el valor máximo dentro de las columnasDuration
-
En lugar de
max_col_name = df.filter(like=''Dur'', axis=1).max().idxmax()
, para devolver el nombre de la columna donde se produce el valor máximo - Lo hice porque mi cerebro confundido me dijo que estaba devolviendo el valor máximo de los nombres de columna, en lugar del valor máximo en la columna. Por ejemplo:
-
test = [''Duration5'', ''Duration2'', ''Duration3'']
print(max(test))
>>> ''Duration5''
- Esta es la razón por la cual estar cansado es una mala condición para resolver problemas
-
Con el sueño y el café, una solución más eficiente.
-
Similar a otros, en el uso de
idmax
-
Similar a otros, en el uso de
Solución nueva y mejorada:
# column name with max duration value
max_col_name = df.filter(like=''Dur'', axis=1).max().idxmax()
# index of max_col_name
max_col_idx =df.columns.get_loc(max_col_name)
# row index of max value in max_col_name
max_row_idx = df[max_col_name].idxmax()
# output with .loc
df.iloc[max_row_idx, [0, max_col_idx, max_col_idx + 1 ]]
Salida:
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Métodos utilizados:
Tengo un Dataframe como:
Sequence Duration1 Value1 Duration2 Value2 Duration3 Value3
1001 145 10 125 53 458 33
1002 475 20 175 54 652 45
1003 685 57 687 87 254 88
1004 125 54 175 96 786 96
1005 475 21 467 32 526 32
1006 325 68 301 54 529 41
1007 125 97 325 85 872 78
1008 129 15 429 41 981 82
1009 547 47 577 52 543 83
1010 666 65 722 63 257 87
Quiero encontrar el valor máximo de Duración en (Duración1, Duración2, Duración3) y devolver el Valor y la secuencia correspondientes.
Mi salida deseada:
Sequence,Duration3,Value3
1008, 981, 82
Aquí hay otra manera,
m=df.set_index(''Sequence'') #set Sequence as index
n=m.filter(like=''Duration'') #gets all columns with the name Duration
s=n.idxmax()[n.eq(n.values.max()).any()]
#output Duration3 1008
d = dict(zip(m.columns[::2],m.columns[1::2])) #create a mapper dict
#{''Duration1'': ''Value1'', ''Duration2'': ''Value2'', ''Duration3'': ''Value3''}
final=m.loc[s.values,s.index.union(s.index.map(d))].reset_index()
Sequence Duration3 Value3
0 1008 981 82
Con datos anchos, puede ser más fácil reformar primero con
wide_to_long
.
Esto crea 2 columnas
[''Duration'', ''Value'']
, y el MultiIndex nos dice qué número era.
No se depende de ningún orden de columnas específico.
import pandas as pd
df = pd.wide_to_long(df, i=''Sequence'', j=''num'', stubnames=[''Duration'', ''Value''])
df.loc[[df.Duration.idxmax()]]
Duration Value
Sequence num
1008 3 981 82
Pruebe el siguiente código bastante corto, basado principalmente en Numpy :
vv = df.iloc[:, 1::2].values
iRow, iCol = np.unravel_index(vv.argmax(), vv.shape)
iCol = iCol * 2 + 1
result = df.iloc[iRow, [0, iCol, iCol + 1]]
El resultado es una serie :
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Si desea "reformarlo" (primero valores de índice, luego valores reales), puede obtener algo como esto ejecutándose:
pd.DataFrame([result.values], columns=result.index)
Puede obtener el índice del valor máximo de una columna usando:
>>> idx = df[''Duration3''].idxmax()
>>> idx
7
Y las columnas relevantes solo usan:
>>> df_cols = df[[''Sequence'', ''Duration3'', ''Value3'']]
>>> df_cols.loc[idx]
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Entonces, simplemente envuelva todo eso en una buena función:
def get_max(df, i):
idx = df[f''Duration{i}''].idxmax()
df_cols = df[[''Sequence'', f''Duration{i}'', f''Value{i}'']]
return df_cols.loc[idx]
Y
1..3
sobre
1..3
:
>>> max_rows = [get_max(i) for i in range(1, 4)]
>>> print(''/n/n''.join(map(str, max_rows)))
Sequence 1003
Duration1 685
Value1 57
Name: 2, dtype: int64
Sequence 1010
Duration2 722
Value2 63
Name: 9, dtype: int64
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Si desea reducir estos 3 a una sola fila máxima, puede hacer lo siguiente:
>>> pairs = enumerate(max_rows, 1)
>>> by_duration = lambda x: x[1][f''Duration{x[0]}'']
>>> i, max_row = max(pairs, key=by_duration)
>>> max_row
Sequence 1008
Duration3 981
Value3 82
Name: 7, dtype: int64
Si entiendo la pregunta correctamente, dado el siguiente marco de datos:
df = pd.DataFrame(data={''Seq'': [1, 2, 3], ''Dur1'': [2, 7, 3],''Val1'': [''x'', ''y'', ''z''],''Dur2'': [3, 5, 1], ''Val2'': [''a'', ''b'', ''c'']})
Seq Dur1 Val1 Dur2 Val2
0 1 2 x 3 a
1 2 7 y 5 b
2 3 3 z 1 c
Estas 5 líneas de código resuelven su problema:
dur_col = [col_name for col_name in df.columns if col_name.startswith(''Dur'')] # [''Dur1'', ''Dur2'']
max_dur_name = df.loc[:, dur_col].max().idxmax()
val_name = "Val" + str([int(s) for s in max_dur_name if s.isdigit()][0])
filter_col = [''Seq'', max_dur_name, val_name]
df_res = df[filter_col].sort_values(max_dur_name, ascending=False).head(1)
Y obtienes:
Seq Dur1 Val1
1 2 7 y
Explicación del código:
Obtengo automáticamente las columnas que comienzan con ''Dur'', y encuentro el nombre de la columna con una duración más larga:
dur_col = [col_name for col_name in df.columns if col_name.startswith(''Dur'')] # [''Dur1'', ''Dur2'']
max_dur_name = df.loc[:, dur_col].max().idxmax()
val_name = "Val" + str([int(s) for s in max_dur_name if s.isdigit()][0])
Elija las columnas que me interesan:
filter_col = [''Seq'', max_dur_name, val_name]
Filtre las columnas que me interesan, ordeno
max_dur_name
y obtengo el resultado de búsqueda:
df_res = df[filter_col].sort_values(max_dur_name, ascending=False).head(1)
# output:
Seq Dur1 Val1
1 2 7 y
Un poco similar a la respuesta de @ Massifox , pero creo que es lo suficientemente diferente como para ser digno de ser agregado.
mvc = df[[name for name in df.columns if ''Duration'' in name]].max().idxmax()
mvidx = df[mvc].idxmax()
valuecol = ''Value'' + mvc[-1]
df.loc[mvidx, [''Sequence'', mvc, valuecol]]
-
Primero obtengo el nombre de columna
mvc
donde se encuentra el valor máximo (mvc
es''Durantion3''
siguiendo su ejemplo). -
Luego obtengo el índice de fila
mvidx
del valor máximo (mvidx
es7
). -
Luego construyo la columna Value correcta (
valuecol
es''Value3''
). -
Finalmente con
loc
selecciono la salida deseada, que es:Sequence 1008 Duration3 981 Value3 82 Name: 7, dtype: int64
if len(df[df[dur1]>=df[dur2].max()])==0:
if len(df[df[dur2]>=df[dur3].max()])==0:
print(df[df[dur3].idmax()][[seq,dur3,val3]])
else:
print(df[df[dur2].idmax()][[seq,dur2,val2]])
else:
if len(df[df[dur1]>=df[dur3].max()])==0:
print(df[df[dur3].idmax()][[seq,dur3,val3]])
else:
print(df[df[dur1].idmax()][[seq,dur1,val1]])