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