簡體   English   中英

如何在 Python 中生成帶有事件的隨機數據系列?

[英]How would I generate a random data series in Python with events?

我正在嘗試為異常檢測生成一個隨機數據序列(或時間序列),事件跨越幾個連續的數據點。 它們可能是高於/低於某個閾值的值,或者是具有不同已知概率的異常類型。

例如,在 1 為正常且事件類型在 [2, 3, 4] 范圍內的情況下: 11112221113333111111112211111

我查看了np.randomrandom方法,但找不到任何生成這些事件的方法。 我目前的解決方案是選擇隨機點,向它們添加隨機持續時間以生成事件開始和結束位置,用隨機事件類型標記每個事件,然后重新連接到數據集,例如:

import numpy as np
num_events = np.random.randint(1, 10)
number_series = [1]*60
first_pos = 0 
event_starts = sorted([first_pos + i for i in np.random.randint(50, size = num_events)])
event_ends = [sum(i) for i in list(zip(event_starts, np.random.randint(8, size = num_events)))]
for c in list(zip(event_starts, event_ends)):
    rand_event_type  = np.random.choice(a = [2, 3, 4], p = [0.5, 0.3, 0.2])
    number_series[c[0]:c[1]] = [rand_event_type]*len(number_series[c[0]:c[1]])
print(number_series)

[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

但我想知道是否有一種更簡單的方法可以根據一組概率生成一系列帶有事件的數字。

這完全取決於您如何為流程建模(您要模擬的底層流程)。 您可以在 Wikipedia 上閱讀更多關於一些常用模型的信息。

最簡單的

在下文中,我們使用一個非常簡單的模型(與您的模型略有不同):每個事件都有一個概率(如您的問題)和一個獨立於事件本身的隨機持續時間。 1 (“正常”)是與其他任何事件一樣的事件(與您的示例代碼不同)。 我們可以改變它,但現在這是你能想到的最簡單的模型之一。

def gen_events(n):
    events = np.random.choice(a=[1, 2, 3, 4], p=[0.6, 0.2, 0.12, 0.08], size=n)
    durations = np.random.randint(1, 8, size=n)
    return np.repeat(events, durations)

np.random.seed(0)  # repeatable example
number_series = gen_events(10)  # for example

>>> number_series
array([1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 2, 1, 1, 1, 1, 1, 1, 3, 4, 4, 1, 1, 1, 1, 1])

請注意,這非常快:

%timeit gen_events(1_000_000)
# 44.9 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

馬爾可夫鏈

另一個模型(更容易參數化,實現起來更復雜)是馬爾可夫模型 其中最簡單的是馬爾可夫鏈。 這是一個超級簡單(但不是很有效)的版本:

def markov_chain(P, n, initial_state=0):
    m = P.shape[0]
    ix = np.arange(m)
    s = np.empty(n, dtype=int)
    s[0] = initial_state
    for i in range(1, n):
        s[i] = np.random.choice(ix, p=P[s[i-1]])
    return s

上面, P是一個轉移矩陣,其中每個單元P[i,j]是從狀態i轉移到狀態j的概率。 這是一個示例應用程序:

P = np.array([
    [.7, .1, .12, .08],  # from 0 to others
    [.3, .6, .05, .05],
    [.3, 0, .65, .05],
    [.4, 0, .05, .55],
])

np.random.seed(0)
n = 100
s = markov_chain(P, n) + 1
>>> s
array([1, 1, 2, 2, 2, 2, 2, 2, 2, 4, 1, 2, 2, 2, 3, 1, 1, 1, 3, 3, 3, 4,
       4, 4, 4, 1, 1, 1, 4, 4, 3, 1, 2, 2, 2, 1, 1, 1, 1, 4, 4, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 3, 1, 3, 1, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 4, 1, 1, 1, 2, 1, 1, 1, 1, 3])

請注意,每個事件的一元概率稱為pi並對應於lim_{k -> \infty} P**k的任何行:

>>> pd.Series(markov_chain(P, 1000, 0)).value_counts(normalize=True).sort_index()
0    0.530
1    0.135
2    0.209
3    0.126

>>> np.linalg.matrix_power(P, 40)[0]
array([0.52188552, 0.13047138, 0.21632997, 0.13131313])

一種不那么冗長的方法是在旅途中生成您的事件列表。

例如,設置異常發生的概率(例如 5%)。 然后,

events = []
for i in range(60):
  if random() <= 0.95:
    events.append(1)
  else:
    events.extend([choice(a = [2, 3, 4], p = [0.5, 0.3, 0.2])] * randint(8))

您可以從 [0, 1) 上的均勻分布生成隨機數,並使用numpy.select並選擇哪個數字將是1, 2, 3, 4如下所示:

import numpy as np
def generate_random_data_series(num, prob=[0.6,0.2,0.05,0.15]):
    x = np.random.rand(num)
    prob = np.cumsum(np.asarray(prob))
    condlist = [
        x < prob[0], 
        x < prob[1], 
        x < prob[2], 
        x < prob[3]
    ]
    choicelist = [1,2,3,4]
    return np.select(condlist, choicelist, default=1)

colab的基准測試:

%timeit generate_random_data_series(1_000_000)
# 25.1 ms per loop (10 loops, best of 5)

測試功能:

>>> from collections import Counter
>>> res = generate_random_data_series(100)
>>> res
array([1, 1, 4, 1, 4, 1, 1, 1, 4, 1, 3, 4, 4, 1, 1, 1, 1, 4, 1, 1, 2, 1,
       4, 1, 1, 1, 1, 1, 2, 1, 1, 4, 2, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 2,
       1, 2, 2, 1, 1, 4, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 4, 1,
       1, 1, 2, 1, 1, 1, 1, 4, 1, 4, 2, 4, 4, 4, 2, 3, 2, 2, 2, 2, 1, 1,
       2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1])
>>> Counter(res)
Counter({1: 61, 4: 15, 3: 3, 2: 21})
# prob  1 : 60%
# count 1 : 61 in 100 random number
# prob  2 : 20%
# count 2 : 21 in 100 random number
# prob  3 : 5%
# count 3 : 3 in 100 random number
# prob  4 : 15%
# count 4 : 15 in 100 random number

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM