2015-01-09 24 views
6

Tôi cần trích xuất tất cả các chuỗi của một chuỗi thời gian/mảng của một cửa sổ cụ thể. Ví dụ: phương phápTách chuỗi Python (chuỗi thời gian/mảng) thành các chuỗi có chồng chéo

>>> ts = pd.Series([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 
>>> window = 3 
>>> subsequences(ts, window) 
array([[0, 1, 2], 
     [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5], 
     [4, 5, 6], 
     [5, 6, 7], 
     [5, 7, 8], 
     [6, 8, 9]]) 

Naive đó lặp qua các chuỗi là tất nhiên đắt, ví dụ:

def subsequences(ts, window): 
    res = [] 
    for i in range(ts.size - window + 1): 
     subts = ts[i:i+window] 
     subts.reset_index(drop=True, inplace=True) 
     subts.name = None 
     res.append(subts) 
    return pd.DataFrame(res) 

Tôi tìm thấy một cách tốt hơn bằng cách sao chép chuỗi, thay đổi nó bằng một giá trị khác nhau cho đến khi cửa sổ được bao phủ và tách các trình tự khác nhau với reshape. Hiệu suất là khoảng 100x tốt hơn, bởi vì cho lặp loop trên kích thước cửa sổ, và không phải là kích thước trình tự:

def subsequences(ts, window): 
    res = [] 
    for i in range(window): 
     subts = ts.shift(-i)[:-(ts.size%window)].reshape((ts.size // window, window)) 
     res.append(subts) 
    return pd.DataFrame(np.concatenate(res, axis=0)) 

I have seen that gấu trúc bao gồm một số chức năng lăn trong module pandas.stats.moment, và tôi đoán những gì họ làm là bằng cách nào đó tương tự như vấn đề sau này. Có bất kỳ nơi nào trong mô-đun đó hay bất kỳ nơi nào khác trong gấu trúc để làm điều này hiệu quả hơn không?

Cảm ơn bạn!

UPDATE (SOLUTION):

Dựa trên câu trả lời @elyase, đối với trường hợp cụ thể này có một thực hiện một chút đơn giản, hãy để tôi viết nó xuống đây, và giải thích những gì nó làm:

def subsequences(ts, window): 
    shape = (ts.size - window + 1, window) 
    strides = ts.strides * 2 
    return np.lib.stride_tricks.as_strided(ts, shape=shape, strides=strides) 

Với mảng numpy 1-D, trước tiên chúng tôi tính toán hình dạng của mảng kết quả. Chúng ta sẽ có một hàng bắt đầu từ mỗi vị trí của mảng, chỉ với ngoại lệ của vài phần tử cuối cùng, lúc bắt đầu chúng sẽ không có đủ yếu tố bên cạnh để hoàn thành cửa sổ.

Xem ví dụ đầu tiên trong mô tả này, cách số cuối cùng chúng tôi bắt đầu là 6, vì bắt đầu từ 7, chúng tôi không thể tạo cửa sổ gồm ba phần tử. Vì vậy, số hàng là kích thước trừ đi cửa sổ cộng với một hàng. Số cột chỉ đơn giản là cửa sổ.

Tiếp theo, phần khó khăn là nói cách điền vào mảng kết quả, với hình dạng chúng tôi vừa xác định.

Để chúng tôi xem xét yếu tố đầu tiên sẽ là phần tử đầu tiên. Sau đó, chúng ta cần xác định hai giá trị (trong một bộ gồm hai số nguyên làm tham số cho tham số strides). Các giá trị xác định các bước chúng ta cần thực hiện trong mảng ban đầu (mảng 1-D) để điền vào phần thứ hai (phần 2-D).

Hãy xem xét một ví dụ khác, nơi chúng tôi muốn triển khai hàm np.reshape, từ mảng 9 phần tử 1-D, tới mảng 3x3. Phần tử đầu tiên lấp đầy vị trí đầu tiên, và sau đó, phần tử ở bên phải của nó, sẽ là phần tiếp theo trên mảng 1-D, vì vậy chúng tôi di chuyển 1 bước. Sau đó, phần khó khăn, để điền vào phần tử đầu tiên của hàng thứ hai, chúng ta nên làm 3 bước, từ 0 đến 4, xem:

>>> original = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8]) 
>>> new = array([[0, 1, 2], 
       [3, 4, 5], 
       [6, 7, 8])] 

Vì vậy, để reshape, các bước của chúng tôi cho hai chiều sẽ là (1, 3). Đối với trường hợp của chúng tôi, nơi nó tồn tại chồng lên nhau, nó thực sự đơn giản hơn. Khi chúng ta di chuyển sang phải để điền vào mảng kết quả, chúng ta bắt đầu ở vị trí tiếp theo trong mảng 1-D, và khi chúng ta di chuyển sang phải, chúng ta sẽ lấy phần tử tiếp theo, 1 bước, trong mảng 1-D. Vì vậy, các bước sẽ là (1, 1).

Chỉ có một điều cuối cùng cần lưu ý.Đối số strides không chấp nhận "các bước" mà chúng tôi đã sử dụng, nhưng thay vào đó là byte trong bộ nhớ. Để biết chúng, chúng tôi có thể sử dụng phương pháp strides của mảng có nhiều mảng. Nó trả về một tuple với các bước tiến (các bước theo byte), với một phần tử cho mỗi chiều. Trong trường hợp của chúng tôi, chúng tôi nhận được một phần tử tuple 1 và chúng tôi muốn nó hai lần, vì vậy chúng tôi có số * 2.

Chức năng np.lib.stride_tricks.as_strided thực hiện thao tác điền bằng cách sử dụng phương pháp được mô tả mà không cần sao chép dữ liệu, làm cho dữ liệu này hoạt động khá hiệu quả.

Cuối cùng, lưu ý rằng hàm được đăng ở đây giả định mảng đầu vào 1-D (khác với mảng 2-D có 1 phần tử là hàng hoặc cột). Xem phương pháp hình dạng của mảng đầu vào và bạn sẽ nhận được một cái gì đó như (N,) và không phải là (N, 1). Phương pháp này sẽ thất bại về sau. Lưu ý rằng phương thức được đăng bởi @elyase xử lý hai mảng thứ nguyên đầu vào (đó là lý do tại sao phiên bản này hơi đơn giản hơn).

+0

khi bạn nói phương pháp ngây thơ là đắt tiền, tôi giả định rằng bạn đã thực sự lược tả chương trình của bạn và đó thực sự là một nút cổ chai? –

+1

Có, khi tôi cần lặp lại toàn bộ chuỗi, không có tối ưu hóa trong tính toán, và nó là chậm. Đối với một chuỗi gồm 4719 phần tử và một cửa sổ là 5, nó mất khoảng 700 mili giây. Cách tiếp cận thứ hai, cho cùng một dữ liệu mất khoảng 8 mili giây. Câu hỏi đặt ra là nếu gấu trúc (hoặc numpy) có thể làm điều đó mà không cần phải lặp lại ở tất cả, mà nên được vẫn còn nhanh hơn. –

+1

bạn có thể có may mắn hơn tại codereview.stackexchange.com Tôi sẽ đặt thông tin thời gian của bạn lên đó trong câu hỏi cũng như –

Trả lời

8

Đây là 34x nhanh hơn so với phiên bản nhanh chóng của bạn trong máy của tôi:

def rolling_window(a, window): 
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window) 
    strides = a.strides + (a.strides[-1],) 
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) 

>>> rolling_window(ts.values, 3) 
array([[0, 1, 2], 
     [1, 2, 3], 
     [2, 3, 4], 
     [3, 4, 5], 
     [4, 5, 6], 
     [5, 6, 7], 
     [6, 7, 8], 
     [7, 8, 9]]) 

tín dụng đi vào Erik Rigtorp.

+0

Cảm ơn rất nhiều elyase! Giải pháp của bạn cũng nhanh hơn trong máy tính của tôi, nhưng có vẻ như hầu hết lợi ích là do tính toán được thực hiện trong numpy, thay vì gấu trúc. Nếu trong giải pháp của bạn, tôi chuyển đổi mảng numpy trở về một con gấu trúc DataFrame đạt được là khoảng 10%, đó là xa 34x, nhưng nó là tốt. Nếu tôi chuyển đổi giải pháp của tôi thành gọn gàng, hiệu suất của giải pháp của bạn vẫn tốt hơn, nhưng chỉ một chút. Hãy để tôi để lại câu hỏi vẫn còn mở, để xem nếu vẫn còn một giải pháp nhanh hơn. Cảm ơn bạn! –

+0

Có thể thay đổi nó để chuyển tiếp bằng các quan sát 'N', trái ngược với' 1' (như được thực hiện trong câu trả lời của bạn)? Tôi chơi xung quanh một chút nhưng không thể quản lý để làm cho nó hoạt động. – Rhubarb

+1

Xin chào @Rhubarb, tôi đã chơi với mã và tạo [gist] (https://gist.github.com/sa2812/1cc7889f10c4d340faf68cbe78fd94b9) để phản ánh những thay đổi đối với hàm ở trên – sunny

Các vấn đề liên quan