Панды: рассчитать разницу между максимальным и минимальным за период времени

Мне нужно рассчитать разницу между максимальным и минимальным значением за период в 1 секунду, фрейм данных выглядит так, эпоха в миллисекундах.

Колонка А Эпоха 10 1373981385937 11 1373981386140 13 1373981386312 8 1373981386968 7 1373981387187 7 1373981387421

Мне нужно создать новый столбец diff, который представляет собой разницу между минимальным и максимальным значением 'column A' в каждом интервале в 1 секунду. Обратите внимание, что все эти интервалы относятся к минимальному значению 'Epoch' (первое значение, 1373981385937, в приведенном выше примере). Сначала я получаю первый интервал в 1 секунду, начиная с 1373981385937, добавляю 1 секунду, получаю значения в этом диапазоне, вычисляю максимальную минимальную разницу и устанавливаю diff на это значение для всего диапазона, сохраняя исходный индекс.

Желаемый результат:

Колонка А Эпоха разница 10 1373981385937 3 11 1373981386140 3 13 1373981386312 3 8 1373981386968 1 7 1373981387187 1 7 1373981387421 1

Ниже я показываю, как я это делаю сейчас:

current_index = 0
list_indexes = []
list_values = []
interval = 1000 # ms
while current_index < series.shape[0]:
    left = series.loc[(series["Epoch"] >= series["Epoch"].iloc[current_index]) & (series["Epoch"] < series["Epoch"].iloc[current_index] + interval)]
    value = left["Column A"].max() - left["Column A"].min()
    list_indexes.extend(list(left.index.values))
    list_values.extend(np.full(left.shape[0], value))
    current_index += left.shape[0]
result = pds.Series(data = list_values, index = list_indexes, name = label, dtype=np.float64)

Я получаю ожидаемый результат, но производительность оставляет желать лучшего.

Есть ли способ сделать это быстрее/лучше?

Редактировать:

Спасибо за поддержку, но я не могу интегрировать решение в свой код отчасти потому, что мне нужно учесть еще два столбца.

Колонка А Колонка Б Колонка С Эпоха разница 25 10 15 1373973055796 5 25 10 10 1373973055828 5 .. .. .. ............. . 25 12 18 1373973092296 2 25 12 16 1373973092328 2 .. .. .. ............. . 26 10 15 1373973055875 4 26 10 11 1373973055906 4 .. .. .. ............. . 26 12 13 1373973092359 3 26 12 10 1373973092406 3 .. .. .. ............. . 27 10 23 1373973055953 6 27 10 17 1373973056000 6 .. .. .. ............. . 27 12 17 1373973092921 7 27 12 10 1373973092953 7

Как я это делаю сейчас:

 for each unique value in colum A
  for each unique value in colum B
   gb = df.groupby((df["Epoch"] - df["Epoch"].min()) // 1000)["Column C"]
   kwargs = {label : gb.transform(max) - gb.transform(min)}
   newdf = df.assign(**kwargs)

Извините за долгое редактирование. Вы думаете, что есть лучший способ?

почему вы получаете 1/1/1 за последние 3? не должны ли они группироваться вместе?

mozway 20.02.2023 16:04

@mozway: я только что понял, что ОП хочет, чтобы 1-секундные интервалы были привязаны к Epoch.min(). @ catalin_345323: я попытался прояснить вопрос и подчеркнуть происхождение 1-секундных интервалов. Пожалуйста, вернитесь, если это не то, что вы имели в виду.

Pierre D 20.02.2023 18:09

@Pierre хорошо, кажется разумным

mozway 20.02.2023 18:57

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

Pierre D 21.02.2023 21:05
Почему в 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
4
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Приведенный ниже код обрабатывает 1 миллион строк примерно за 151 мс (на универсальном процессоре Intel Xeon Platinum 8175M).

Используя ваш пример:

gb = df.groupby((df['Epoch'] - df['Epoch'].min()) // 1000)['Column A']
newdf = df.assign(diff=gb.transform(max) - gb.transform(min))

>>> newdf
   Column A          Epoch  diff
0        10  1373981385937     3
1        11  1373981386140     3
2        13  1373981386312     3
3         8  1373981386968     1
4         7  1373981387187     1
5         7  1373981387421     1

Быстрая проверка: ничего из нижеперечисленного не требуется для решения выше, а просто для того, чтобы убедиться, что результат правильный. Мы назначаем t фактическую дату и время, а delta_t разницу в секундах с t.min():

t = pd.to_datetime(df['Epoch'], unit='ms')
tmp = df.assign(
    t=t,
    delta_t=(t - t.min()).dt.total_seconds(),
    groupno=gb.ngroup(),
)
>>> tmp
   Column A          Epoch                       t  delta_t  groupno
0        10  1373981385937 2013-07-16 13:29:45.937    0.000        0
1        11  1373981386140 2013-07-16 13:29:46.140    0.203        0
2        13  1373981386312 2013-07-16 13:29:46.312    0.375        0
3         8  1373981386968 2013-07-16 13:29:46.968    1.031        1
4         7  1373981387187 2013-07-16 13:29:47.187    1.250        1
5         7  1373981387421 2013-07-16 13:29:47.421    1.484        1

Редактировать: использовать первый, последний вместо мин, макс

В ответ на комментарий: «Есть ли способ заменить функцию преобразования max/min чем-то, что возвращает первое и последнее значение группы?»

Да:

newdf = df.assign(diff=gb.transform('last') - gb.transform('first'))
>>> newdf
   Column A          Epoch  diff
0        10  1373981385937     3
1        11  1373981386140     3
2        13  1373981386312     3
3         8  1373981386968    -1
4         7  1373981387187    -1
5         7  1373981387421    -1

Скорость

n = 1_000_000
t0 = 1373981385937
df = pd.DataFrame({
    'Column A': np.random.randint(0, 100, n),
    'Epoch': np.random.randint(t0, t0 + 300 * n, n),
})

def f(df):
    gb = df.groupby((df['Epoch'] - df['Epoch'].min()) // 1000)['Column A']
    return df.assign(diff=gb.transform(max) - gb.transform(min))

%timeit f(df)
# 151 ms ± 1.87 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Приложение: дополнительные столбцы (измененный вопрос)

IIUC, вы хотели бы сделать тот же расчет, но в каждой группе значений [A, B]. Происхождение секундных интервалов времени по-прежнему является глобальным минимумом Epoch.

def f(df):
    gb = df.groupby(
        ['Column A', 'Column B', (df['Epoch'] - df['Epoch'].min()) // 1000]
    )['Column C']
    return df.assign(diff=gb.transform(max) - gb.transform(min))

Пример предоставленных выборочных данных с двумя дополнительными столбцами:

df = pd.DataFrame({
    'Column A': [25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27],
    'Column B': [10, 10, 12, 12, 10, 10, 12, 12, 10, 10, 12, 12],
    'Column C': [15, 10, 18, 16, 15, 11, 13, 10, 23, 17, 17, 10],
    'Epoch': [
        1373973055796, 1373973055828, 1373973092296, 1373973092328,
        1373973055875, 1373973055906, 1373973092359, 1373973092406,
        1373973055953, 1373973056000, 1373973092921, 1373973092953],
})

>>> f(df)
    Column A  Column B  Column C          Epoch  diff
0         25        10        15  1373973055796     5
1         25        10        10  1373973055828     5
2         25        12        18  1373973092296     2
3         25        12        16  1373973092328     2
4         26        10        15  1373973055875     4
..       ...       ...       ...            ...   ...
7         26        12        10  1373973092406     3
8         27        10        23  1373973055953     6
9         27        10        17  1373973056000     6
10        27        12        17  1373973092921     7
11        27        12        10  1373973092953     7

Скорость

### Speed
n = 1_000_000
t0 = 1373981385937
df = pd.DataFrame({
    'Column A': np.random.randint(0, 10, n),
    'Column B': np.random.randint(0, 10, n),
    'Column C': np.random.randint(0, 10, n),
    'Epoch': np.random.randint(t0, t0 + 300 * n, n),
})

%timeit f(df)
# 538 ms ± 4.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

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

catalin_345323 22.02.2023 07:15

Есть ли способ заменить функцию преобразования max/min чем-то, что возвращает первое и последнее значение группы? Мне это нужно для чего-то похожего, но вместо разницы между max и min мне нужна разница между последним и первым элементом. Я пробовал использовать такую ​​функцию, как def get_last(df): return df.iloc[-1], но она очень медленная.

catalin_345323 22.02.2023 13:34

Да. Смотрите «редактировать» в ответе.

Pierre D 22.02.2023 16:14

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