Мой фрейм данных:
import pandas as pd
df = pd.DataFrame(
{
'high': [100, 102, 120, 150, 125, 115],
'target': [99, 105, 118, 108, 140, 141]
}
)
Ожидаемый результат — создание столбца x
:
high target x
0 100 99 99
1 102 105 99
2 120 118 118
3 150 108 118
4 125 140 118
5 115 141 108
Я объясняю это построчно. Я начинаю со строки 1
, потому что так легче объяснить.
102 следует сравнить со всеми target
, которые стоят перед ним или в той же строке. То есть 102 следует сравнить с 99 и 105. Из этих двух значений я хочу найти максимальное значение, которое МЕНЬШЕ или РАВНО, чем 102. Максимальное значение равно 99. Поэтому это значение выбрано для x
.
Для строки 2
120 следует сравнить с 99, 105 и 118. И снова применяется та же логика, и значение x
равно 105.
Для строки 3
150 следует сравнить с 99, 105, 118 и 108. Все эти значения меньше 150, но максимальное значение — 118.
Это моя попытка, которая не удалась:
import numpy as np
bins = df.target.sort_values(ascending=True).to_list()
bins = bins + [np.inf]
labels = bins[:-1]
df['x'] = pd.cut(df.high, include_lowest=True, right=False, labels=labels, bins=bins)
@jezrael Что-то около 70 тысяч,
более производительным вариантом было бы запустить цикл for на языке более низкого уровня (или скомпилировать jit - numba)
@sammywemmy Спасибо за ваше предложение. Можете ли вы опубликовать свой комментарий в качестве ответа с примером кода или подсказкой?
df['x'] = [
df.loc[:i, 'target']
.where(lambda x: x.le(df.loc[i, 'high']))
.max()
for i in df.index
]
Решение Numpy с трансляцией - сравните меньше или равно, выбрав только строки над существующими значениями по другой маске m2
, если не совпадает, замените на nan
и получите максимум с помощью numpy.nanmax:
t = df['target'].to_numpy()
h = df['high'].to_numpy()
m1 = np.arange(len(t))[:,None] >= np.arange(len(t))
m2 = t <= h[:, None]
df['x'] = np.nanmax(np.where(m1 & m2, t, np.nan), axis=1)
print (df)
high target x
0 100 99 99.0
1 102 105 99.0
2 120 118 118.0
3 150 108 118.0
4 125 140 118.0
5 115 141 108.0
Как это работает:
print (m1)
[[ True False False False False False]
[ True True False False False False]
[ True True True False False False]
[ True True True True False False]
[ True True True True True False]
[ True True True True True True]]
print (m2)
[[ True False False False False False]
[ True False False False False False]
[ True True True True False False]
[ True True True True True True]
[ True True True True False False]
[ True True False True False False]]
print (m1 & m2)
[[ True False False False False False]
[ True False False False False False]
[ True True True False False False]
[ True True True True False False]
[ True True True True False False]
[ True True False True False False]]
print (np.where(m1 & m2, t, np.nan))
[[ 99. nan nan nan nan nan]
[ 99. nan nan nan nan nan]
[ 99. 105. 118. nan nan nan]
[ 99. 105. 118. 108. nan nan]
[ 99. 105. 118. 108. nan nan]
[ 99. 105. nan 108. nan nan]]
print (np.nanmax(np.where(m1 & m2, t, np.nan), axis=1))
[ 99. 99. 118. 118. 118. 108.]
Этот ответ явно более эффективен, чем мой ответ (цикл). Я рекомендую спрашивающему выбрать этот ответ. Следует отметить, что существует проблема с оперативной памятью. Я являюсь пользователем совместной работы, и он не будет превышать 50 тыс. строк данных.
@PandaKim - да, абсолютно согласен, если данные очень большие, должна быть проблема с оперативной памятью.
это один из способов сделать это, используя numba:
# pip install numba
import numba
@numba.njit()
def max_val(arr1, arr2, result):
lo = 0
hi = arr1.size
max_value = arr2[0]
start = 0
while lo < hi:
value = arr1[lo]
if max_value > value:
max_value = arr2[0]
start = 0
for n in range(start, lo+1):
val = arr2[n]
if val>value:
continue
if val > max_value:
max_value = val
start = n
result[lo]=max_value
lo += 1
return result
t = df['target'].to_numpy()
h = df['high'].to_numpy()
result = np.empty(t.size, dtype=t.dtype)
df['x'] = max_val(arr1=h,arr2=t,result=result)
df
high target x
0 100 99 99
1 102 105 99
2 120 118 118
3 150 108 118
4 125 140 118
5 115 141 108
Это должно помочь вам создать более производительный цикл (используйте целые числа без знака для индексации, возможно, распараллеливайте, используйте специальный язык более низкого уровня...)
import pandas as pd
import numpy as np
# Create the DataFrame
df = pd.DataFrame(
{
'high': [100, 102, 120, 150, 125, 115],
'target': [99, 105, 118, 108, 140, 141]
}
)
# Convert columns to numpy arrays
t = df['target'].to_numpy()
h = df['high'].to_numpy()
# Step 1: Create a full comparison matrix
comparison_matrix = t <= h[:, None]
print("Comparison Matrix:")
print(comparison_matrix)
'''
Comparison Matrix:
[[ True False False False False False]
[ True False False False False False]
[ True True True True False False]
[ True True True True True True]
[ True True True True False False]
[ True True False True False False]]
'''
# Step 2: Create a lower triangular mask
lower_triangular_mask = np.tril(comparison_matrix)
print("\nLower Triangular Mask:")
print(lower_triangular_mask)
'''
Lower Triangular Mask:
[[ True False False False False False]
[ True False False False False False]
[ True True True False False False]
[ True True True True False False]
[ True True True True False False]
[ True True False True False False]]
'''
# Step 3: Apply the mask and replace False values with NaN
masked_targets = np.where(lower_triangular_mask, t, np.nan)
print("\nMasked Targets:")
print(masked_targets)
'''
Masked Targets:
[[ 99. nan nan nan nan nan]
[ 99. nan nan nan nan nan]
[ 99. 105. 118. nan nan nan]
[ 99. 105. 118. 108. nan nan]
[ 99. 105. 118. 108. nan nan]
[ 99. 105. nan 108. nan nan]]
'''
# Step 4: Find the maximum value in each row, ignoring NaNs
df['x'] = np.nanmax(masked_targets, axis=1)
print("\nDataFrame with 'x' column:")
print(df)
'''
DataFrame with 'x' column:
high target x
0 100 99 99.0
1 102 105 99.0
2 120 118 118.0
3 150 108 118.0
4 125 140 118.0
5 115 141 108.0
'''
Каково количество строк в ваших реальных данных?