2013-05-01 34 views
7

Cập nhật: bắt đầu với phiên bản 0.20.0, gấu trúc cắt/qcut KHÔNG xử lý các trường ngày. Xem What's New để biết thêm.Tương đương với cắt/qcut cho các trường ngày của gấu trúc là gì?

pd.cut và pd.qcut bây giờ hỗ trợ datetime64 và timedelta64 dtypes (GH14714, GH14798)

câu hỏi gốc: Pandas cắt và qcut chức năng là tuyệt vời cho 'bucketing' dữ liệu liên tục cho sử dụng trong các bảng tổng hợp và vv, nhưng tôi không thể thấy một cách dễ dàng để lấy các trục datetime trong hỗn hợp. Bực bội vì gấu trúc thật tuyệt vời ở mọi thứ liên quan đến thời gian!

Dưới đây là một ví dụ đơn giản:

def randomDates(size, start=134e7, end=137e7): 
    return np.array(np.random.randint(start, end, size), dtype='datetime64[s]') 

df = pd.DataFrame({'ship' : randomDates(10), 'recd' : randomDates(10), 
        'qty' : np.random.randint(0,10,10), 'price' : 100*np.random.random(10)}) 
df 

    price  qty recd    ship 
0 14.723510 3 2012-11-30 19:32:27 2013-03-08 23:10:12 
1 53.535143 2 2012-07-25 14:26:45 2012-10-01 11:06:39 
2 85.278743 7 2012-12-07 22:24:20 2013-02-26 10:23:20 
3 35.940935 8 2013-04-18 13:49:43 2013-03-29 21:19:26 
4 54.218896 8 2013-01-03 09:00:15 2012-08-08 12:50:41 
5 61.404931 9 2013-02-10 19:36:54 2013-02-23 13:14:42 
6 28.917693 1 2012-12-13 02:56:40 2012-09-08 21:14:45 
7 88.440408 8 2013-04-04 22:54:55 2012-07-31 18:11:35 
8 77.329931 7 2012-11-23 00:49:26 2012-12-09 19:27:40 
9 46.540859 5 2013-03-13 11:37:59 2013-03-17 20:09:09 

Để bin bởi nhóm giá hoặc số lượng, tôi có thể sử dụng cắt/qcut bộ chứa chúng:

df.groupby([pd.cut(df['qty'], bins=[0,1,5,10]), pd.qcut(df['price'],q=3)]).count() 

         price qty recd ship 
qty  price    
(0, 1] [14.724, 46.541] 1 1 1 1 
(1, 5] [14.724, 46.541] 2 2 2 2 
     (46.541, 61.405] 1 1 1 1 
(5, 10] [14.724, 46.541] 1 1 1 1 
     (46.541, 61.405] 2 2 2 2 
     (61.405, 88.44] 3 3 3 3 

Nhưng tôi không thể nhìn thấy bất kỳ dễ dàng cách làm điều tương tự với các trường ngày 'recd' hoặc 'ship' của tôi. Ví dụ: tạo một bảng tính tương tự được chia nhỏ theo (nhóm) hàng tháng của recd và tàu. Nó có vẻ như resample() có tất cả các máy móc để xô vào các giai đoạn, nhưng tôi không thể tìm ra cách áp dụng nó ở đây. Các xô (hoặc các cấp) trong 'ngày cắt' sẽ tương đương với một pandas.PeriodIndex, và sau đó tôi muốn gắn nhãn mỗi giá trị của df ['recd'] với khoảng thời gian nó rơi vào?

Vì vậy, các loại đầu ra tôi đang tìm kiếm sẽ là một cái gì đó như:

ship recv  count 
2011-01 2011-01 1 
     2011-02 3 
     ...  ... 
2011-02 2011-01 2 
     2011-02 6 
...  ...  ... 

Tổng quát hơn, tôi muốn để có thể trộn và kết hợp các biến liên tục hoặc phân loại trong đầu ra. Hãy tưởng tượng df cũng chứa một cột 'tình trạng' với/giá trị vàng/xanh đỏ, sau đó có lẽ tôi muốn tóm tắt đếm theo tình trạng, xô giá, tàu và xô recd, vì vậy:

ship recv  price status count 
2011-01 2011-01 [0-10) green  1 
          red  4 
       [10-20) yellow  2 
        ...  ... ... 
     2011-02 [0-10) yellow  3 
     ...  ...  ... ... 

Như một câu hỏi bonus, có chuyện gì cách đơn giản nhất để sửa đổi kết quả groupby() ở trên chỉ chứa một cột đầu ra đơn lẻ được gọi là 'đếm'?

Trả lời

4

Đây là giải pháp sử dụng gấu trúc.PeriodIndex (báo trước: PeriodIndex không dường như hỗ trợ quy tắc thời gian với nhiều> 1, chẳng hạn như '4M'). Tôi nghĩ rằng câu trả lời cho câu hỏi thưởng của bạn là .

In [49]: df.groupby([pd.PeriodIndex(df.recd, freq='Q'), 
    ....:    pd.PeriodIndex(df.ship, freq='Q'), 
    ....:    pd.cut(df['qty'], bins=[0,5,10]), 
    ....:    pd.qcut(df['price'],q=2), 
    ....:   ]).size() 
Out[49]: 
       qty  price 
2012Q2 2013Q1 (0, 5] [2, 5] 1 
2012Q3 2013Q1 (5, 10] [2, 5] 1 
2012Q4 2012Q3 (5, 10] [2, 5] 1 
     2013Q1 (0, 5] [2, 5] 1 
       (5, 10] [2, 5] 1 
2013Q1 2012Q3 (0, 5] (5, 8] 1 
     2013Q1 (5, 10] (5, 8] 2 
2013Q2 2012Q4 (0, 5] (5, 8] 1 
     2013Q2 (0, 5] [2, 5] 1 
4

Chỉ cần thiết lập các chỉ số của lĩnh vực bạn muốn Resample bởi, đây là một số ví dụ

In [36]: df.set_index('recd').resample('1M',how='sum') 
Out[36]: 
       price qty 
recd      
2012-07-31 64.151194 9 
2012-08-31 93.476665 7 
2012-09-30 94.193027 7 
2012-10-31   NaN NaN 
2012-11-30   NaN NaN 
2012-12-31 12.353405 6 
2013-01-31   NaN NaN 
2013-02-28 129.586697 7 
2013-03-31   NaN NaN 
2013-04-30   NaN NaN 
2013-05-31 211.979583 13 

In [37]: df.set_index('recd').resample('1M',how='count') 
Out[37]: 
2012-07-31 price 1 
      qty  1 
      ship  1 
2012-08-31 price 1 
      qty  1 
      ship  1 
2012-09-30 price 2 
      qty  2 
      ship  2 
2012-10-31 price 0 
      qty  0 
      ship  0 
2012-11-30 price 0 
      qty  0 
      ship  0 
2012-12-31 price 1 
      qty  1 
      ship  1 
2013-01-31 price 0 
      qty  0 
      ship  0 
2013-02-28 price 2 
      qty  2 
      ship  2 
2013-03-31 price 0 
      qty  0 
      ship  0 
2013-04-30 price 0 
      qty  0 
      ship  0 
2013-05-31 price 3 
      qty  3 
      ship  3 
dtype: int64 
+1

Đây không phải là giải pháp chung, ví dụ: nếu tôi muốn nhóm vào hai ngày khác nhau hoặc một ngày và không phải là ngày (thông qua biến cắt hoặc danh mục). Tôi sẽ cập nhật câu hỏi với cấu trúc đầu ra mà tôi đang tìm kiếm. – patricksurry

0

Làm thế nào về việc sử dụng Series và đưa các bộ phận của DataFrame mà bạn quan tâm vào đó, sau đó gọi cut trên đối tượng chuỗi?

price_series = pd.Series(df.price.tolist(), index=df.recd) 

và sau đó

pd.qcut(price_series, q=3) 

và vân vân. (Mặc dù tôi nghĩ câu trả lời của @ Jeff là tốt nhất)

1

Tôi nghĩ ra một ý tưởng dựa trên định dạng lưu trữ cơ bản của datetime64 [ns].Nếu bạn xác định dcut() như thế này

def dcut(dts, freq='d', right=True): 
    hi = pd.Period(dts.max(), freq=freq) + 1 # get first period past end of data 
    periods = pd.PeriodIndex(start=dts.min(), end=hi, freq=freq) 
    # get a list of integer bin boundaries representing ns-since-epoch 
    # note the extra period gives us the extra right-hand bin boundary we need 
    bounds = np.array(periods.to_timestamp(how='start'), dtype='int') 
    # bin our time field as integers 
    cut = pd.cut(np.array(dts, dtype='int'), bins=bounds, right=right) 
    # relabel the bins using the periods, omitting the extra one at the end 
    cut.levels = periods[:-1].format() 
    return cut 

Sau đó, chúng ta có thể làm những gì tôi muốn:

df.groupby([dcut(df.recd, freq='m', right=False),dcut(df.ship, freq='m', right=False)]).count() 

Để nhận được:

   price qty recd ship 
2012-07 2012-10 1 1 1 1 
2012-11 2012-12 1 1 1 1 
     2013-03 1 1 1 1 
2012-12 2012-09 1 1 1 1 
     2013-02 1 1 1 1 
2013-01 2012-08 1 1 1 1 
2013-02 2013-02 1 1 1 1 
2013-03 2013-03 1 1 1 1 
2013-04 2012-07 1 1 1 1 
     2013-03 1 1 1 1 

Tôi đoán bạn tương tự có thể định nghĩa dqcut() mà đầu tiên "làm tròn" mỗi giá trị datetime cho số nguyên đại diện cho sự bắt đầu của khoảng thời gian có chứa của nó (tại tần số xác định của bạn), và sau đó sử dụng qcut() để chọn giữa các ranh giới đó. Hoặc làm qcut() trước tiên trên các giá trị nguyên nguyên và làm tròn các thùng kết quả dựa trên tần suất bạn đã chọn?

Không có niềm vui nào về câu hỏi thưởng chưa? :)

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