2012-10-18 41 views
9

Tôi có một dataframe có cấu trúc như thế này:Thêm một cột với một groupby trên dataframe thứ bậc

First  A        B       
Second bar  baz  foo  bar  baz  foo  
Third cat dog cat dog cat dog cat dog cat dog cat dog 
0   3 8 7 7 4 7 5 3 2 2 6 2 
1   8 6 5 7 8 7 1 8 6 0 3 9 
2   9 2 2 9 7 3 1 8 4 1 0 8 
3   3 6 0 6 3 2 2 6 2 4 6 9 
4   7 6 4 3 1 5 0 4 8 4 8 1 

Vì vậy, có ba cấp độ cột. Tôi muốn thêm một cột mới ở cấp độ thứ hai, nơi cho mỗi cấp độ thứ ba tính toán được thực hiện, ví dụ 'new' = 'foo' + 'bar'. Vì vậy, các dataframe kết quả sẽ trông giống như:

First  A          B         
Second bar  baz  foo  new  bar  baz  foo  new  
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog 
0   3 8 7 7 4 7 7 15 5 3 2 2 6 2 11 5 
1   8 6 5 7 8 7 16 13 1 8 6 0 3 9 4 17 
2   9 2 2 9 7 3 16 5 1 8 4 1 0 8 1 16 
3   3 6 0 6 3 2 6 8 2 6 2 4 6 9 8 15 
4   7 6 4 3 1 5 8 11 0 4 8 4 8 1 8 5 

Tôi đã tìm thấy một workaround được liệt kê ở phần cuối của bài viết này, nhưng không phải ở tất cả của nó 'gấu trúc kiểu' và dễ bị lỗi. Các chức năng áp dụng hoặc chuyển đổi trên một nhóm có vẻ như đúng cách để đi nhưng sau giờ cố gắng tôi vẫn không thành công. Tôi đã tìm đúng cách nên giống như sau:

def func(data): 

    fi = data.columns[0][0] 
    th = data.columns[0][2] 

    data[(fi,'new',th)] = data[(fi,'foo',th)] + data[(fi,'bar',th)] 

    print data 
    return data 

print grouped.apply(func) 

Cột mới được thêm chính xác trong hàm, nhưng không được trả lại. Sử dụng cùng chức năng với phép biến đổi sẽ hoạt động nếu cột 'mới' đã tồn tại trong df, nhưng làm cách nào để bạn thêm cột mới ở cấp cụ thể 'đang di chuyển' hoặc trước khi nhóm?

Mã để tạo ra df mẫu là:

import pandas, itertools 

first = ['A','B'] 
second = ['foo','bar','baz'] 
third = ['dog', 'cat'] 

tuples = [] 
for tup in itertools.product(first, second, third): 
    tuples.append(tup) 

columns = pandas.MultiIndex.from_tuples(tuples, names=['First','Second','Third']) 

data = np.random.randint(0,10,(5, 12)) 
df = pandas.DataFrame(data, columns=columns) 

Và cách giải quyết của tôi:

dfnew = None 
grouped = df.groupby(by=None, level=[0,2], axis=1) 

for name, group in grouped: 
    newparam = group.xs('foo', axis=1, level=1) + group.xs('bar', axis=1, level=1) 

    dftmp = group.join(pandas.DataFrame(np.array(newparam), columns=pandas.MultiIndex.from_tuples([(group.columns[0][0], 'new', group.columns[0][2])], names=['First','Second', 'Third']))) 

    if dfnew is None: 
     dfnew = dftmp 
    else: 
     dfnew = pandas.concat([dfnew, dftmp], axis=1) 

print dfnew.sort_index(axis=1) 

Mà hoạt động, nhưng việc tạo ra một dataframe mới cho mỗi nhóm và 'bằng tay' gán mức là một thực hành tồi tệ.

Vì vậy, cách thích hợp để làm điều này là gì? Tôi tìm thấy một số bài viết đối phó với các câu hỏi tương tự, nhưng tất cả các bài viết này chỉ có 1 cấp độ cột, và đó chính xác là những gì tôi đang đấu tranh với.

+0

Tạo một cột mới dựa trên các giá trị nhóm là một nhiệm vụ cho chuyển đổi , nhưng tôi không biết nếu tranform có thể xuất nhiều cột. Tôi sẽ giải quyết điều này giống như cách bạn đã làm. BTW dưới mui xe, biến đổi cũng tạo ra một khung mới cho mỗi nhóm và concats tất cả ở cuối. –

+0

Có cơ chế áp dụng/biến đổi có thể xuất các giá trị có cấu trúc và các giá trị được truyền vào colums (tức là nếu một tuple được tạo ra bởi hàm được áp dụng, các thành phần đi trong các cột riêng biệt thay vì tuple trở thành một phần tử nguyên tử trong một cột) là một tính năng tuyệt vời, ngay cả khi nó chỉ là cú pháp. Có lẽ với một tên phương thức khác, để làm cho ý định rõ ràng (applicationfork hoặc một cái gì đó như thế, hoặc một từ khóa splitseq = True trong áp dụng). – meteore

Trả lời

7

Chắc chắn có điểm yếu trong API ở đây nhưng tôi không chắc chắn đầu của đầu của tôi để làm cho nó dễ dàng hơn để làm những gì bạn đang làm. Dưới đây là một trong những cách đơn giản xung quanh này, ít nhất là ví dụ của bạn:

In [20]: df 
Out[20]: 
First  A        B       
Second foo  bar  baz  foo  bar  baz  
Third dog cat dog cat dog cat dog cat dog cat dog cat 
0   7 2 9 3 3 0 5 9 8 2 0 6 
1   1 4 1 7 2 3 2 3 1 0 4 0 
2   6 5 0 6 6 1 5 1 7 4 3 6 
3   4 8 1 9 0 3 9 2 3 1 5 9 
4   6 1 1 5 1 2 2 6 3 7 2 1 

In [21]: rdf = df.stack(['First', 'Third']) 

In [22]: rdf['new'] = rdf.foo + rdf.bar 

In [23]: rdf 
Out[23]: 
Second   bar baz foo new 
    First Third      
0 A  cat  3 0 2 5 
     dog  9 3 7 16 
    B  cat  2 6 9 11 
     dog  8 0 5 13 
1 A  cat  7 3 4 11 
     dog  1 2 1 2 
    B  cat  0 0 3 3 
     dog  1 4 2 3 
2 A  cat  6 1 5 11 
     dog  0 6 6 6 
    B  cat  4 6 1 5 
     dog  7 3 5 12 
3 A  cat  9 3 8 17 
     dog  1 0 4 5 
    B  cat  1 9 2 3 
     dog  3 5 9 12 
4 A  cat  5 2 1 6 
     dog  1 1 6 7 
    B  cat  7 1 6 13 
     dog  3 2 2 5 

In [24]: rdf.unstack(['First', 'Third']) 
Out[24]: 
Second bar     baz     foo     new    
First  A   B   A   B   A   B   A   B  
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog 
0   3 9 2 8 0 3 6 0 2 7 9 5 5 16 11 13 
1   7 1 0 1 3 2 0 4 4 1 3 2 11 2 3 3 
2   6 0 4 7 1 6 6 3 5 6 1 5 11 6 5 12 
3   9 1 1 3 3 0 9 5 8 4 2 9 17 5 3 12 
4   5 1 7 3 2 1 1 2 1 6 6 2 6 7 13 5 

Và bạn có thể dĩ nhiên sắp xếp lại nội dung của trái tim của bạn:

In [28]: rdf.unstack(['First', 'Third']).reorder_levels(['First', 'Second', 'Third'], axis=1).sortlevel(0, axis=1) 
Out[28]: 
First  A          B         
Second bar  baz  foo  new  bar  baz  foo  new  
Third cat dog cat dog cat dog cat dog cat dog cat dog cat dog cat dog 
0   3 9 0 3 2 7 5 16 2 8 6 0 9 5 11 13 
1   7 1 3 2 4 1 11 2 0 1 0 4 3 2 3 3 
2   6 0 1 6 5 6 11 6 4 7 6 3 1 5 5 12 
3   9 1 3 0 8 4 17 5 1 3 9 5 2 9 3 12 
4   5 1 2 1 1 6 6 7 7 3 1 2 6 2 13 5 
+0

Cảm ơn Wes, điều này tốt hơn nhiều. –

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