У меня есть фрейм данных с 7 столбцами. Я хочу создать 8-й столбец с выводом уравнения. Уравнение: из столбцов 2-7 для тех чисел, которые выше столбца 1, выведите минимальное значение в столбец 8.
В Excel я бы использовал эту формулу в Col8: =MINIFS($Col2:$Col7,$Col2:$Col7,">"&$Col1)
Date_Time Col1 Col2 Col3 Col4 Col5 Col6 Col7
20 3/14/2022 0:35 0.68053 0.68048 0.68094 0.6811 0.68111 0.68126 0.68179
21 3/14/2022 0:36 0.6805 0.6805 0.68091 0.68109 0.68111 0.68125 0.68178
22 3/14/2022 0:37 0.68052 0.68051 0.68089 0.68107 0.6811 0.68124 0.68177
23 3/14/2022 0:38 0.68057 0.6805 0.68086 0.68106 0.6811 0.68123 0.68176
24 3/14/2022 0:39 0.68055 0.6805 0.68083 0.68104 0.68109 0.68122 0.68175
Выход приведенного выше образца данных будет следующим:
Col8
0.68094
0.68091
0.68089
0.68086
0.68083
Я прочитал много сообщений и попробовал с np.where:
ColList = [Col2,Col3,Col4,Col5,Col6,Col7]
df['Col8'] = np.where(df[ColList ].min(axis=1) > df['Col1'],df[ColList ].min(axis=1),np.nan)
Но он находит минимальное число в списке, а затем проверяет, больше ли это минимальное число, чем Col1. Что я ищу, так это сделать это в обратном порядке: чтобы он смотрел только на числа, которые больше, чем Col1, и возвращал минимум из этих чисел (или возвращал np.nan, если нет чисел больше, чем Col1).
Я предпочитаю векторизованное решение, так как набор данных довольно большой. В противном случае я бы просто создал функцию/цикл для этого.
IIUC, вам нужны значения положительно ближайший. Один из способов использования маскировки:
s = df[["Col1"]].values
tmp = df.loc[:, "Col2":] - s
tmp[tmp<=0] = np.inf
df["Col8"] = tmp.min(axis=1) + s.ravel()
Выход:
print(df["Col8"])
20 0.68094
21 0.68091
22 0.68089
23 0.68086
24 0.68083
Name: Col8, dtype: float64
Логика:
Col1
и другими.Col1
) заменяются на numpy.inf
, чтобы исключить минимальное сравнение.Спектакль:
df = df.sample(n=100000, replace=True).reset_index(drop=True)
%%timeit
s = df[["Col1"]].values
tmp = df.loc[:, "Col2":] - s
tmp[tmp<=0] = np.inf
df["Col8"] = tmp.min(axis=1) + s.ravel()
# 13.1 ms ± 379 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Один из вариантов — сравнить в numpy, а затем использовать pd.where
и min(axis=1)
, чтобы получить желаемый результат:
left = df.filter(regex=r"[2-7]")
right = df.Col1.to_numpy()[:, None]
booleans = left.to_numpy() > right
left.where(booleans).min(axis = 1)
0.68094
0.68091
0.68089
0.68086
0.68083
Я не смог хорошо скопировать ваши данные (проблемы со столбцом даты), поэтому вывод серии.