2015-05-04 31 views
19

Tôi có khung dữ liệu gấu trúc mydf có hai cột và cả hai cột là kiểu dữ liệu datetime: mydatemytime. Tôi muốn thêm ba cột khác: hour, weekdayweeknum.Thêm nhiều cột vào Pandas khung dữ liệu từ hàm

def getH(t): #gives the hour 
    return t.hour 
def getW(d): #gives the week number 
    return d.isocalendar()[1] 
def getD(d): #gives the weekday 
    return d.weekday() # 0 for Monday, 6 for Sunday 

mydf["hour"] = mydf.apply(lambda row:getH(row["mytime"]), axis=1) 
mydf["weekday"] = mydf.apply(lambda row:getD(row["mydate"]), axis=1) 
mydf["weeknum"] = mydf.apply(lambda row:getW(row["mydate"]), axis=1) 

Đoạn mã hoạt động nhưng không hiệu quả về mặt tính toán khi nó vòng qua khung dữ liệu ít nhất ba lần. Tôi chỉ muốn biết nếu có một cách nhanh hơn và/hoặc tối ưu hơn để làm điều này. Ví dụ: sử dụng zip hoặc merge? Ví dụ, nếu tôi chỉ tạo một hàm trả về ba phần tử, tôi nên thực hiện điều này như thế nào? Để minh họa, các chức năng sẽ là:

def getHWd(d,t): 
    return t.hour, d.isocalendar()[1], d.weekday() 
+0

có thể trùng lặp của [? Có thể thêm nhiều cột cùng một lúc để một DataFrame gấu trúc] (http : //stackoverflow.com/questions/19866377/is-it-possible-to-add-several-columns-at-once-to-a-pandas-dataframe) – geekazoid

Trả lời

9

Để bổ sung cho John Galt's answer:

Tùy thuộc vào nhiệm vụ đó được thực hiện bởi lambdafunc , bạn có thể gặp phải một số tăng tốc bằng cách lưu trữ kết quả của apply trong một DataFrame mới d sau đó tham gia với bản gốc:

lambdafunc = lambda x: pd.Series([x['mytime'].hour, 
            x['mydate'].isocalendar()[1], 
            x['mydate'].weekday()]) 

newcols = df.apply(lambdafunc, axis=1) 
newcols.columns = ['hour', 'weekday', 'weeknum'] 
newdf = df.join(newcols) 

Ngay cả khi bạn không thấy cải thiện tốc độ, tôi khuyên bạn nên sử dụng join. Bạn sẽ có thể để tránh sự (luôn luôn gây phiền nhiễu) SettingWithCopyWarning có thể bật lên khi gán trực tiếp trên cột:

SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame. 
Try using .loc[row_indexer,col_indexer] = value instead 
19

Dưới đây là cách tiếp cận để làm điều đó bằng một apply

Say, df là như

In [64]: df 
Out[64]: 
     mydate  mytime 
0 2011-01-01 2011-11-14 
1 2011-01-02 2011-11-15 
2 2011-01-03 2011-11-16 
3 2011-01-04 2011-11-17 
4 2011-01-05 2011-11-18 
5 2011-01-06 2011-11-19 
6 2011-01-07 2011-11-20 
7 2011-01-08 2011-11-21 
8 2011-01-09 2011-11-22 
9 2011-01-10 2011-11-23 
10 2011-01-11 2011-11-24 
11 2011-01-12 2011-11-25 

Chúng tôi sẽ mất chức năng lambda ra để tách dòng để dễ đọc và xác định nó như

In [65]: lambdafunc = lambda x: pd.Series([x['mytime'].hour, 
              x['mydate'].isocalendar()[1], 
              x['mydate'].weekday()]) 

Và, apply và lưu trữ các kết quả để df[['hour', 'weekday', 'weeknum']]

In [66]: df[['hour', 'weekday', 'weeknum']] = df.apply(lambdafunc, axis=1) 

Và, kết quả là như

In [67]: df 
Out[67]: 
     mydate  mytime hour weekday weeknum 
0 2011-01-01 2011-11-14  0  52  5 
1 2011-01-02 2011-11-15  0  52  6 
2 2011-01-03 2011-11-16  0  1  0 
3 2011-01-04 2011-11-17  0  1  1 
4 2011-01-05 2011-11-18  0  1  2 
5 2011-01-06 2011-11-19  0  1  3 
6 2011-01-07 2011-11-20  0  1  4 
7 2011-01-08 2011-11-21  0  1  5 
8 2011-01-09 2011-11-22  0  1  6 
9 2011-01-10 2011-11-23  0  2  0 
10 2011-01-11 2011-11-24  0  2  1 
11 2011-01-12 2011-11-25  0  2  2 
+0

Cảm ơn, John. Có vẻ tốt. Cách tiếp cận này hoạt động nhanh hơn so với cách tiếp cận ban đầu. Đối với khung dữ liệu có ~ 500 hàng, hiệu suất trung bình cho phương pháp tiếp cận của bạn là 0,1446926 giây, trong khi hiệu suất ban đầu mất 0,15949020 giây, trung bình (10 lần chạy). – EFL

2
def getWd(d): 
    d.isocalendar()[1], d.weekday() 
def getH(t): 
    return t.hour 
mydf["hour"] = zip(*df["mytime"].map(getH)) 
mydf["weekday"], mydf["weeknum"] = zip(*df["mydate"].map(getWd)) 
+0

Venkat, hi. Đoạn mã trả về một đối số TypeError: zip # 1 phải hỗ trợ lặp lại – EFL

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