Получить максимальное значение, сравнивая несколько столбцов и вернуть конкретные значения

У меня есть датафрейм, как:

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

Я хочу найти максимальное значение Duration в (Duration1, Duration2, Duration3) и вернуть соответствующее значение & Последовательность.

Мой желаемый результат:

Sequence,Duration3,Value3
1008,    981,      82

Ответы

Ответ 1

Попробуйте следующий, довольно короткий код, основанный в основном на 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]]

Результатом является серия:

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

Если вы хотите "изменить его" (сначала значения индекса, затем фактические значения), вы можете получить что-то вроде этого, выполнив:

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

Ответ 2

С широкими данными может быть легче сначала изменить форму с wide_to_long. Это создает 2 столбца ['Duration', 'Value'], и MultiIndex сообщает нам, какой это был номер. Там нет опоры на какой-либо конкретный порядок столбцов.

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

Ответ 3

Вы можете получить индекс максимального значения столбца, используя:

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

И соответствующие столбцы только с использованием:

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

Итак, просто оберните все это в одну красивую функцию:

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]

И переберите 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

Если вы хотите уменьшить эти 3 до одной максимальной строки, вы можете сделать следующее:

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

Если я правильно понимаю вопрос, предоставьте следующий фрейм данных:

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

Эти 5 строк кода решают вашу проблему:

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)

И вы получаете:

   Seq  Dur1 Val1 
1    2     7    y  

Объяснение кода:

Я автоматически получаю столбцы, начинающиеся с 'Dur', и нахожу имя столбца с большей продолжительностью:

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]

Отфильтруйте интересующие меня столбцы, я заказываю для max_dur_name и получаю результат поиска:

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

# output:
   Seq  Dur1 Val1 
1    2     7    y   

Ответ 5

Вот еще один способ,

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

Ответ 6

Без использования мастера numpy:

  • Во-первых, есть некоторые действительно отличные решения этой проблемы, другие.
  • Данные будут предоставлены в вопросе, как 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]]

Выход:

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

Обновление

  • Прошлой ночью, фактически в 4 часа утра, я отказался от лучшего решения, потому что был слишком уставшим.
    • Я использовал max_value = max(df.filter(like='Dur', axis=1).max().tolist()), чтобы вернуть максимальное значение в столбцах Duration
    • Вместо max_col_name = df.filter(like='Dur', axis=1).max().idxmax(), чтобы вернуть имя столбца, в котором встречается максимальное значение
    • Я сделал это, потому что мой сложенный мозг сказал мне, что я возвращаю максимальное значение имен столбцов вместо максимального значения в столбце. Например:
test = ['Duration5', 'Duration2', 'Duration3']
print(max(test))
>>> 'Duration5'
  • Вот почему переутомление является плохим условием для решения проблем
  • Со сном и кофе - более эффективное решение
    • Подобно другим, в использовании idmax

Новый & Улучшенное решение:

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

Выход:

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

Используемые методы:

Ответ 7

Немного похоже на @Massifox answer, но я думаю, что оно достаточно отличается, чтобы быть достойным добавления.

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. Сначала я получаю имя столбца mvc, в котором находится максимальное значение (в соответствии с вашим примером mvc - 'Durantion3').
  2. Затем я получаю индекс строки mvidx максимального значения (mvidx - 7).
  3. Затем я строю правильный столбец значения (valuecol - 'Value3').
  4. Наконец, с помощью loc я выбираю желаемый выход, а именно:

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

Ответ 8

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