2012-10-24 39 views
11

Tôi đang cố gắng chuyển đổi DataFrame, sao cho một số hàng sẽ được nhân rộng một số lần nhất định. Ví dụ:gấu trúc: áp dụng hàm cho DataFrame có thể trả về nhiều hàng

df = pd.DataFrame({'class': ['A', 'B', 'C'], 'count':[1,0,2]}) 

    class count 
0  A  1 
1  B  0 
2  C  2 

nên được chuyển thành:

class 
0  A 
1  C 
2  C 

Đây là mặt trái của sự kết hợp với chức năng đếm. Có một cách dễ dàng để đạt được nó trong gấu trúc (không sử dụng cho vòng hoặc danh sách comprehensions)?

Một khả năng có thể là cho phép DataFrame.applymap trả về nhiều hàng hàm (akin apply phương thức GroupBy). Tuy nhiên, tôi không nghĩ rằng nó có thể có trong gấu trúc bây giờ.

+0

Tôi cũng lưu ý một chức năng chung cho phép trả về nhiều, một hoặc 0 hàng tùy thuộc vào giá trị trong cột 'đếm'. – btel

+0

Nếu bạn đến với câu hỏi này vào năm 2017+, hãy kiểm tra câu trả lời của tôi để có giải pháp hiệu quả và dễ hiểu hơn. –

Trả lời

17

Bạn có thể sử dụng groupby:

def f(group): 
    row = group.irow(0) 
    return DataFrame({'class': [row['class']] * row['count']}) 
df.groupby('class', group_keys=False).apply(f) 

để bạn có được

In [25]: df.groupby('class', group_keys=False).apply(f) 
Out[25]: 
    class 
0  A 
0  C 
1  C 

Bạn có thể sửa chữa các chỉ số kết quả tuy nhiên bạn thích

+0

Điều đó giải quyết được vấn đề của tôi! Cảm ơn Wes. – btel

+1

Câu trả lời hay! Nếu tôi có hàng tá cột khác, có cách nào dễ dàng để bảo toàn các cột đó trong kết quả của 'f' ngoài việc nhập chúng một cách rõ ràng không? –

1
repeated_items = [list(row[1]*row[2]) for row in df.itertuples()] 

sẽ tạo ra một danh sách lồng nhau:

[['A'], [], ['C', 'C']] 

mà sau đó bạn có thể duyệt qua với comprehensions danh sách để tạo ra một khung dữ liệu mới:

new_df = pd.DataFrame({"class":[j for i in repeated_items for j in i]}) 

Tất nhiên, bạn có thể làm điều đó trong một dòng cũng như nếu bạn muốn:

new_df = pd.DataFrame({"class":[j for i in [list(row[1]*row[2]) for row in df.itertuples()] for j in i]}) 
3

Tôi biết đây là một câu hỏi cũ, nhưng tôi đã có gặp rắc rối khi nhận được câu trả lời của Wes để làm việc cho nhiều cột trong khung dữ liệu, vì vậy tôi đã làm cho mã của anh ta chung chung hơn một chút. Nghĩ rằng tôi sẽ chia sẻ trong trường hợp bất cứ ai khác tình cờ gặp câu hỏi này với cùng một vấn đề.

Bạn chỉ về cơ bản xác định cột nào có số đếm trong đó và bạn nhận lại một khung dữ liệu mở rộng.

import pandas as pd 
df = pd.DataFrame({'class 1': ['A','B','C','A'], 
        'class 2': [ 1, 2, 3, 1], 
        'count': [ 3, 3, 3, 1]}) 
print df,"\n" 

def f(group, *args): 
    row = group.irow(0) 
    Dict = {} 
    row_dict = row.to_dict() 
    for item in row_dict: Dict[item] = [row[item]] * row[args[0]] 
    return pd.DataFrame(Dict) 

def ExpandRows(df,WeightsColumnName): 
    df_expand = df.groupby(df.columns.tolist(), group_keys=False).apply(f,WeightsColumnName).reset_index(drop=True) 
    return df_expand 


df_expanded = ExpandRows(df,'count') 
print df_expanded 

Returns:

class 1 class 2 count 
0  A  1  3 
1  B  2  3 
2  C  3  3 
3  A  1  1 

    class 1 class 2 count 
0  A  1  1 
1  A  1  3 
2  A  1  3 
3  A  1  3 
4  B  2  3 
5  B  2  3 
6  B  2  3 
7  C  3  3 
8  C  3  3 
9  C  3  3 

Liên quan đến tốc độ, df cơ sở của tôi là 10 cột bằng ~ 6k hàng và khi mở rộng là ~ 100.000 hàng mất ~ 7 giây. Tôi không chắc chắn trong trường hợp này nếu nhóm là cần thiết hoặc khôn ngoan vì nó lấy tất cả các cột để hình thành nhóm, nhưng hey bất cứ điều gì chỉ 7 giây.

0

Câu hỏi này rất cũ và câu trả lời không phản ánh khả năng hiện đại của gấu trúc. Bạn có thể sử dụng iterrows để lặp qua mỗi hàng và sau đó sử dụng hàm tạo DataFrame để tạo Khung dữ liệu mới với số hàng chính xác. Cuối cùng, sử dụng pd.concat để nối tất cả các hàng lại với nhau.

pd.concat([pd.DataFrame(data=[row], index=range(row['count'])) 
      for _, row in df.iterrows()], ignore_index=True) 

    class count 
0  A  1 
1  C  2 
2  C  2 

Điều này có lợi ích khi làm việc với bất kỳ DataFrame kích thước nào.

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