Панды вычисляют скользящее количество последовательных значений в пределах допуска

Скажем, у меня есть список запросов на веб-сайт за несколько дней подряд. Я хочу рассчитать количество дней, в течение которых счетчик запросов текущего дня находится в пределах некоторого допуска (% от счетчика текущего дня).

Синтетический пример:

>>> pd.DataFrame({'req': {0: 15, 1: 16, 2: 14, 3: 15, 4: 16, 5: 16, 6: 17, 7: 30, 8: 31, 9: 35, 10: 32, 11: 35, 12: 34, 13: 33, 14: 37}, 'lo': {0: 13.5, 1: 14.4, 2: 12.6, 3: 13.5, 4: 14.4, 5: 14.4, 6: 15.3, 7: 27.0, 8: 27.9, 9: 31.5, 10: 28.8, 11: 31.5, 12: 30.6, 13: 29.7, 14: 33.3}, 'hi': {0: 16.5, 1: 17.6, 2: 15.4, 3: 16.5, 4: 17.6, 5: 17.6, 6: 18.7, 7: 33.0, 8: 34.1, 9: 38.5, 10: 35.2, 11: 38.5, 12: 37.4, 13: 36.3, 14: 40.7}, 'con10': {0: 0, 1: 1, 2: 0, 3: 3, 4: 1, 5: 2, 6: 2, 7: 0, 8: 1, 9: 0, 10: 3, 11: 2, 12: 4, 13: 6, 14: 0}})
    req    lo    hi  con10
0    15  13.5  16.5      0
1    16  14.4  17.6      1
2    14  12.6  15.4      0
3    15  13.5  16.5      3
4    16  14.4  17.6      1
5    16  14.4  17.6      2
6    17  15.3  18.7      2
7    30  27.0  33.0      0
8    31  27.9  34.1      1
9    35  31.5  38.5      0
10   32  28.8  35.2      3
11   35  31.5  38.5      2
12   34  30.6  37.4      4
13   33  29.7  36.3      6
14   37  33.3  40.7      0

Выше:

  • req — количество запросов за этот день,
  • lo и hi — диапазон допустимых значений для этого дня и
  • con10 — это количество дней подряд, предшествующих этому дню, когда количество запросов находится в пределах заданного допуска (в данном случае 10%).

Любые указатели относительно того, как я мог бы рассчитать con для заданного допуска (или, в более общем плане, списка допусков, то есть con05, con07, con10 для 5/7/10% соответственно)?

Вы можете хотеть показать больше деталей об этом .

BENY 14.12.2020 23:39

@BENY Конечно, как вы думаете, какие детали мне было бы полезно добавить?

levant pied 14.12.2020 23:40

Просто почему строка 1 16 con10 равна 1

BENY 14.12.2020 23:43

@BENY В строке 1 req=16, поэтому диапазон -+10% составляет lo-hi=14,4-17,6. con10=1, потому что столько последовательных строк, которые предшествуют строке 1, попадают в этот диапазон (т.е. строка 0 имеет значение 15, которое находится между 14,4-17,6). Дайте мне знать, если это прояснит ситуацию.

levant pied 14.12.2020 23:49

Как насчет idx 3 с req=15, почему это не 3 в con10?

Cainã Max Couto-Silva 15.12.2020 00:45

Спасибо @CainãMaxCouto-Silva - это была ошибка с моей стороны, я исправил пост.

levant pied 15.12.2020 01:33

Ваше последнее значение тоже должно быть 0, верно!?

Cainã Max Couto-Silva 15.12.2020 02:55

@ CainãMaxCouto-Silva Правильно, я тоже обновил это. Спасибо, что поймали этих двоих!

levant pied 15.12.2020 14:34
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
8
314
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обновлено:

Вот как это сделать (взяв последние предыдущие строки):

def last_within_range(df, target_col='req', tolerance=10):
    
    df = df.copy()
    s = pd.Series(dtype=int, index=df.index)
    
    # Get low and high tolerance
    df['lo'] = df[target_col] - df[target_col] * tolerance/100
    df['hi'] = df[target_col] + df[target_col] * tolerance/100
    
    # Find how many last rows the current value from `req` is within the desired range  
    for idx in df.index[1:]:
        past_idx = df.index[:df.index.get_loc(idx)]
        req = df.loc[idx, 'req']
        # Get bool values and identify groups to get the last one
        values = (req >= df.loc[past_idx, 'lo']) & (req <= df.loc[past_idx, 'hi'])
        grps = (values != values.shift()).cumsum()
        # If the last group is True, then get its sum
        s[idx] = grps.eq(grps.iloc[-1]).sum() if values.iloc[-1] == True else 0
        
    return df.assign(**{f'con{tolerance}': s})

last_within_range(df, tolerance=10)

Выход:

    req    lo    hi  con10
0    15  13.5  16.5      0
1    16  14.4  17.6      1
2    14  12.6  15.4      0
3    15  13.5  16.5      3
4    16  14.4  17.6      1
5    16  14.4  17.6      2
6    17  15.3  18.7      2
7    30  27.0  33.0      0
8    31  27.9  34.1      1
9    35  31.5  38.5      0
10   32  28.8  35.2      3
11   35  31.5  38.5      2
12   34  30.6  37.4      4
13   33  29.7  36.3      6
14   37  33.3  40.7      0

Однако он использует цикл :(


Оригинальный ответ:

Вы можете использовать функцию для вычисления lo и hi, а затем использовать их в цикле по индексам. Взгляните на следующую функцию:

def last_within_range(df, target_col='req', tolerance=10):
    
    df = df.copy()
    s = pd.Series(dtype=int, index=df.index)
    
    # Get low and high tolerance
    df['lo'] = df[target_col] - df[target_col] * tolerance/100
    df['hi'] = df[target_col] + df[target_col] * tolerance/100
    
    # Find how many last rows the current value from `req` is within the desired range  
    for idx in df.index:
        past_idx = df.index[:df.index.get_loc(idx)]
        req = df.loc[idx, 'req']
        s[idx] = (
            ((req >= df.loc[past_idx, 'lo']) & (req <= df.loc[past_idx, 'hi'])).sum()
        )
        
    return s # df.assign(**{f'con{tolerance}': s})

где он возвращает Series в качестве вывода. Так, например:

df['con5'] = last_within_range(df, tolerance=5)
df['con7'] = last_within_range(df, tolerance=7)
df['con10'] = last_within_range(df, tolerance=10)
    req  con5  con7  con10
0    15     0     0      0
1    16     0     1      1
2    14     0     1      1
3    15     1     2      3
4    16     1     3      3
5    16     2     4      4
6    17     0     3      3
7    30     0     0      0
8    31     1     1      1
9    35     0     0      0
10   32     1     2      3
11   35     1     1      2
12   34     2     3      4
13   33     2     5      6
14   37     0     2      3

Обратите внимание, что он не возвращает, сколько последовательных предыдущих строк находится в ожидаемом диапазоне, но возвращает, сколько из предыдущих строк соответствует вашим критериям.

Кроме того, если вы хотите увидеть вывод вместе с вычисленными lo и hi, вместо этого запустите return фрейм данных, заменив оператор return на df.assign(**{f'con{tolerance}': s}), а не s.

Спасибо! Однако последовательное требование важно. Кроме того, поскольку объем данных не является незначительным, есть ли способ избежать части цикла python (for idx in df.index:), то есть сделать это только с пандами?

levant pied 15.12.2020 01:44

Я так не думаю. Хотя pandas предоставляет rolling, вы не ищете конкретный подход к окну. Вам нужно будет проанализировать все предыдущие строки для каждой строки, а затем определить, есть ли группа последних последовательных Trues... Однако можно адаптировать этот код для его достижения (если вам интересно).

Cainã Max Couto-Silva 15.12.2020 01:48

Я обновил вопрос. К сожалению, я не смог найти способ только с пандами, без цикла, но он работает. Дайте мне знать, если у вас есть какие-либо сомнения. Лучший!

Cainã Max Couto-Silva 15.12.2020 02:56

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