Как я могу сравнить значение в одном столбце со всеми значениями, которые находятся ДО него в другом столбце?

Мой фрейм данных:

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 22.05.2024 10:03

@jezrael Что-то около 70 тысяч,

AmirX 22.05.2024 10:26

более производительным вариантом было бы запустить цикл for на языке более низкого уровня (или скомпилировать jit - numba)

sammywemmy 22.05.2024 10:27

@sammywemmy Спасибо за ваше предложение. Можете ли вы опубликовать свой комментарий в качестве ответа с примером кода или подсказкой?

AmirX 22.05.2024 10:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
103
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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 тыс. строк данных.

Panda Kim 22.05.2024 10:02

@PandaKim - да, абсолютно согласен, если данные очень большие, должна быть проблема с оперативной памятью.

jezrael 22.05.2024 10:03

это один из способов сделать это, используя 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
'''

Другие вопросы по теме