web-dev-qa-db-fra.com

Obtenir une valeur maximale en comparant plusieurs colonnes et renvoyer des valeurs spécifiques

J'ai un Dataframe comme:

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

Je veux trouver la valeur maximale de Durée dans (Durée1, Durée2, Durée3) et renvoyer la valeur et la séquence correspondantes.

Ma sortie souhaitée:

Sequence,Duration3,Value3
1008,    981,      82
18
Rohit Lamba K

Essayez le code suivant, assez court, basé principalement sur 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]]

Le résultat est un série:

Sequence     1008
Duration3     981
Value3         82
Name: 7, dtype: int64

Si vous voulez le "remodeler" (d'abord les valeurs d'index, puis les valeurs réelles), vous pouvez obtenir quelque chose comme ceci en exécutant:

pd.DataFrame([result.values], columns=result.index)
15
Valdi_Bo

Avec des données étendues, il peut être plus facile de refaçonner d'abord avec wide_to_long. Cela crée 2 colonnes ['Duration', 'Value'], et le MultiIndex nous indique de quel numéro il s'agit. Il n'y a aucun recours à un ordre de colonne spécifique.

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
5
ALollz

Si je comprends bien la question, compte tenu du cadre de données suivant:

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

Ces 5 lignes de code résolvent votre problème:

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)

Et vous obtenez:

   Seq  Dur1 Val1 
1    2     7    y  

Explication du code:

J'obtiens automatiquement les colonnes qui commencent par "Dur", et je trouve le nom de la colonne avec une durée plus longue:

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])

Choisissez les colonnes qui m'intéressent:

filter_col = ['Seq', max_dur_name, val_name]

Filtrer les colonnes qui m'intéressent, je commande pour max_dur_name et j'obtiens le résultat de la recherche:

df_res = df[filter_col].sort_values(max_dur_name, ascending=False).head(1)

# output:
   Seq  Dur1 Val1 
1    2     7    y   
4
Massifox

Un peu similaire à @ réponse de Massifox , mais je pense que c'est assez différent pour être digne d'être ajouté.

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]]
  1. Je reçois d'abord le nom de la colonne mvc où se trouve la valeur maximale (mvc est 'Durantion3' suivant votre exemple).
  2. Ensuite, j'obtiens l'index de ligne mvidx de la valeur maximale (mvidx est 7).
  3. Ensuite, je crée la colonne de valeur correcte (valuecol est 'Value3').
  4. Enfin avec loc je sélectionne la sortie souhaitée, qui est:

    Sequence     1008
    Duration3     981
    Value3         82
    Name: 7, dtype: int64
    
4
Valentino

Voici une autre façon,

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
4
anky_91

Sans utiliser numpy sorcellerie:

  • Premièrement, il existe de très bonnes solutions à ce problème, par d'autres.
  • Les données seront celles fournies dans la question, comme 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]]

Production:

Sequence     1008
Duration3     981
Value3         82
Name: 7, dtype: int64

Mettre à jour

  • Hier soir, à 4 heures du matin, j'ai rejeté une meilleure solution, car j'étais trop fatigué.
    • J'ai utilisé max_value = max(df.filter(like='Dur', axis=1).max().tolist()), pour renvoyer la valeur maximale dans les colonnes Duration
    • Au lieu de max_col_name = df.filter(like='Dur', axis=1).max().idxmax(), pour renvoyer le nom de la colonne où se trouve la valeur maximale
    • Je l'ai fait parce que mon cerveau égaré m'a dit que je retournais la valeur maximale des noms de colonne, au lieu de la valeur maximale dans la colonne. Par exemple:
test = ['Duration5', 'Duration2', 'Duration3']
print(max(test))
>>> 'Duration5'
  • C'est pourquoi le surmenage est une mauvaise condition de résolution de problèmes
  • Avec le sommeil et le café, une solution plus efficace
    • Semblable à d'autres, dans l'utilisation de idmax

Solution nouvelle et améliorée:

# 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 ]]

Production:

Sequence     1008
Duration3     981
Value3         82
Name: 7, dtype: int64

Méthodes utilisées:

4
Trenton McKinney

Vous pouvez obtenir l'index de la valeur maximale d'une colonne en utilisant:

>>> idx = df['Duration3'].idxmax()
>>> idx
7

Et les colonnes pertinentes utilisant uniquement:

>>> df_cols = df[['Sequence', 'Duration3', 'Value3']]
>>> df_cols.loc[idx]
Sequence     1008
Duration3     981
Value3         82
Name: 7, dtype: int64

Donc, tout simplement envelopper tout cela dans une seule fonction Nice:

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]

Et boucle sur 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 vous souhaitez réduire ces 3 à une seule ligne maximale, vous pouvez effectuer les opérations suivantes:

>>> 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
4
Mateen Ulhaq
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]])
0
vBrail