#python #pandas
#python #pandas
Вопрос:
Допустим, у меня есть список запросов на веб-сайт за последовательные дни. Я хочу рассчитать количество дней, в течение которых количество запросов текущего дня находится в пределах некоторого допуска (% от количества текущих дней).
Синтетический пример:
>>> 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% соответственно)?
Комментарии:
1. Возможно, вы захотите показать более подробную информацию об этом.
2. @BENY Конечно, какие детали, по вашему мнению, мне было бы полезно добавить?
3. Просто почему в строке 1 16 con10 равно 1
4. Как насчет idx 3 с req = 15, почему это не 3 дюйма
con10
?5. Ваше последнее значение также должно быть 0, верно !?
Ответ №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']) amp; (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
Однако он использует цикл: (
Оригинальный ответ:
Вы можете использовать функцию для получения calcule 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']) amp; (req <= df.loc[past_idx, 'hi'])).sum()
)
return s # df.assign(**{f'con{tolerance}': s})
где он возвращает a 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 dataframe , заменив оператор return на df.assign(**{f'con{tolerance}': s})
, а не s
.
Комментарии:
1. Спасибо! Однако важно требование последовательности. Кроме того, поскольку объем данных не является незначительным, есть ли способ избежать части цикла python (
for idx in df.index:
), т. Е. Делать это только с pandas?2. Я так не думаю. Хотя pandas предоставляет a
rolling
, вы не ищете конкретный оконный подход. Вам нужно будет проанализировать все предыдущие строки для каждой строки, а затем определить, есть ли группа последних последовательныхTrue
s… Однако этот код можно адаптировать для его достижения (если вам интересно).3. Я обновил вопрос. К сожалению, я не смог найти способ только с pandas, без цикла, но это работает. Дайте мне знать, если у вас есть какие-либо сомнения. Лучшее!