Оценить частоту, продолжительность и значения временных рядов - PullRequest
3 голосов
/ 12 марта 2019

Я новичок в python и у меня простой вопрос, на который я еще не нашел ответа. Допустим, у меня есть временной ряд с c (t):

t_  c_
1   40
2   41
3   4
4   5
5   7
6   20
7   20
8   8
9   90
10  99
11  10
12  5
13  8
14  8
15  19

Теперь я хочу оценить эту серию в отношении того, как долго значение c непрерывно находилось в определенных диапазонах и как часто происходят эти периоды времени.

Таким образом, результат будет включать три столбца: c (binned), длительность (binned), частота. В переводе на простой пример результат может выглядеть следующим образом:

c_      Dt_  Freq_ 
0-50    8    1 
50-100  2    1
0-50    5    1

Можете ли вы дать мне совет?

Заранее спасибо,

Ульрик

// EDIT: Спасибо за ответы! Данные моего примера были несколько ошибочными, поэтому я не смог показать часть своего вопроса. Итак, вот новый ряд данных:

series=
t   c
1   1
2   1
3   10
4   10
5   10
6   1
7   1
8   50
9   50
10  50
12  1
13  1
14  1

Если я применю код, предложенный Кристофом ниже:

bins = pd.cut(series['c'], [-1, 5, 100])
same_as_prev = (bins != bins.shift())
run_ids = same_as_prev.cumsum()
result = bins.groupby(run_ids).aggregate(["first", "count"])

Я получаю такой результат:

first   count
(-1, 5]   2
(5, 100]  3
(-1, 5]   2
(5, 100]  3
(-1, 5]   3

но что меня больше интересует что-то похожее на это:

c        length  freq
(-1, 5]    2      2
(-1, 5]    3      1
(5, 100]   3      2

Как мне этого добиться? И как я мог построить это на графике KDE?

Best

Ульрик

Ответы [ 2 ]

2 голосов
/ 12 марта 2019

Красиво заданный вопрос с примером :) Это один из способов сделать это, скорее всего, неполный, но он должен вам немного помочь.

Поскольку ваши данные разнесены во времени с фиксированным шагом, я не использую временной ряд и использую индекс как время. Таким образом, я конвертирую c в массив и использую np.where(), чтобы найти значение в ячейках.

import numpy as np

c = np.array([40, 41, 4, 5, 7, 20, 20, 8, 90, 99, 10, 5, 8, 8, 19])

bin1 = np.where((0 <= c) & (c <= 50))[0]
bin2 = np.where((50 < c) & (c <= 100))[0]

Для bin1, вывод равен array([ 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14], dtype=int64), что соответствует idx, где значения из c находятся в корзине.

Следующий шаг - найти последовательный idx. Согласно этому ТАК сообщение ::

from itertools import groupby
from operator import itemgetter

data = bin1
for k, g in groupby(enumerate(data), lambda ix : ix[0] - ix[1]):
    print(list(map(itemgetter(1), g)))

# Output is:
#[0, 1, 2, 3, 4, 5, 6, 7]
#[10, 11, 12, 13, 14]

Последний шаг: разместите новый суббункер в правильном порядке и проследите, какие лотки соответствуют каким суббинам. Таким образом, полный код будет выглядеть так:

import numpy as np
from itertools import groupby
from operator import itemgetter

c = np.array([40, 41, 4, 5, 7, 20, 20, 8, 90, 99, 10, 5, 8, 8, 19])

bin1 = np.where((0 <= c) & (c <= 50))[0]
bin2 = np.where((50 < c) & (c <= 100))[0]

# 1 and 2 for the range names.
bins = [(bin1, 1), (bin2, 2)]
subbins = list()

for b in bins:
    data = b[0]
    name = b[1] # 1 or 2
    for k, g in groupby(enumerate(data), lambda ix : ix[0] - ix[1]):
        subbins.append((list(map(itemgetter(1), g)), name))

subbins = sorted(subbins, key=lambda x: x[0][0])

Выход: [([0, 1, 2, 3, 4, 5, 6, 7], 1), ([8, 9], 2), ([10, 11, 12, 13, 14], 1)]

Тогда вам просто нужно сделать необходимую статистику:)

1 голос
/ 12 марта 2019
import pandas as pd

def bin_run_lengths(series, bins):

    binned = pd.cut(pd.Series(series), bins)
    return binned.groupby(
        (1 - (binned == binned.shift())).cumsum()
    ).aggregate(
        ["first", "count"]
    )

(Я не уверен, куда входит ваш столбец частоты - в задаче, которую вы описываете, кажется, что она всегда будет установлена ​​на 1.)

Binning

Создать серию легко с pandas.cut():

https://pandas.pydata.org/pandas-docs/version/0.23.4/generated/pandas.cut.html

import pandas as pd

pd.cut(pd.Series(range(100)), bins=[-1,0,10,20,50,100])

Контейнеры здесь задаются как (включающие справа, исключающие слева) границы; аргумент может быть дан в разных формах.

0       (-1.0, 0.0]
1       (0.0, 10.0]
2       (0.0, 10.0]
3       (0.0, 10.0]
4       (0.0, 10.0]
5       (0.0, 10.0]
6       (0.0, 10.0]
          ...
19     (10.0, 20.0]
20     (10.0, 20.0]
21     (20.0, 50.0]
22     (20.0, 50.0]
23     (20.0, 50.0]
          ...
29     (20.0, 50.0]
          ...      
99    (50.0, 100.0]
Length: 100, dtype: category
Categories (4, interval[int64]): [(0, 10] < (10, 20] < (20, 50] < (50, 100]]

Преобразует его из ряда значений в ряд интервалов.

Подсчет последовательных значений

Это не имеет родного идиома в пандах, но это довольно легко с несколькими общими функциями. Ответ StackOverflow, получивший наибольшее количество голосов здесь, очень хорошо выглядит: Подсчет последовательных положительных значений в массиве Python

same_as_prev = (series != series.shift())

Это дает логический ряд, который определяет, отличается ли значение от предыдущего.

run_ids = same_as_prev.cumsum()

Это делает серию int, которая увеличивается на 0 каждый раз, когда значение изменяется на новый прогон, и, таким образом, присваивает каждой позиции в серии "идентификатор прогона"

result = series.groupby(run_ids).aggregate(["first", "count"])

Это приводит к кадру данных, который показывает значение в каждом прогоне и длину этого прогона:

      first   count
0   (-1, 0]      1
1   (0, 10]     10
2   (10, 20]    10
3   (20, 50]    30
4   (50, 100]   49
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...