2017-12-12 98 views
5

Lấy DataFrame đồ chơi sau:Bắt quan sát gần đây nhất & ngày từ nhiều cột

data = np.arange(35, dtype=np.float32).reshape(7, 5) 
data = pd.concat((
    pd.DataFrame(list('abcdefg'), columns=['field1']), 
    pd.DataFrame(data, columns=['field2', '2014', '2015', '2016', '2017'])), 
    axis=1) 

data.iloc[1:4, 4:] = np.nan 
data.iloc[4, 3:] = np.nan 

print(data) 
    field1 field2 2014 2015 2016 2017 
0  a  0.0 1.0 2.0 3.0 4.0 
1  b  5.0 6.0 7.0 NaN NaN 
2  c 10.0 11.0 12.0 NaN NaN 
3  d 15.0 16.0 17.0 NaN NaN 
4  e 20.0 21.0 NaN NaN NaN 
5  f 25.0 26.0 27.0 28.0 29.0 
6  g 30.0 31.0 32.0 33.0 34.0 

Tôi muốn thay thế "năm" cột (2014-2017) với hai lĩnh vực: phi gần đây nhất - quan sát đầy đủ và năm tương ứng của quan sát đó. Giả sử field1 là khóa duy nhất. (Tôi không muốn làm bất cứ ops groupby, chỉ cần 1 hàng cho mỗi bản ghi.) Tức là:

field1 field2 obs date 
0  a  0.0 4.0 2017 
1  b  5.0 7.0 2015 
2  c 10.0 12.0 2015 
3  d 15.0 17.0 2015 
4  e 20.0 21.0 2014 
5  f 25.0 29.0 2017 
6  g 30.0 34.0 2017 

tôi đã nhận được này cho đến nay:

pd.melt(data, id_vars=['field1', 'field2'], 
     value_vars=['2014', '2015', '2016', '2017'])\ 
    .dropna(subset=['value']) 

    field1 field2 variable value 
0  a  0.0  2014 1.0 
1  b  5.0  2014 6.0 
2  c 10.0  2014 11.0 
3  d 15.0  2014 16.0 
4  e 20.0  2014 21.0 
5  f 25.0  2014 26.0 
6  g 30.0  2014 31.0 
# ... 

Nhưng đang phải vật lộn với làm thế nào để xoay trở lại theo định dạng mong muốn.

Trả lời

4

Có thể:

d2 = data.melt(id_vars=["field1", "field2"], var_name="date", value_name="obs").dropna(subset=["obs"]) 
d2["date"] = d2["date"].astype(int) 
df = d2.loc[d2.groupby(["field1", "field2"])["date"].idxmax()] 

mà mang lại cho tôi

field1 field2 date obs 
21  a  0.0 2017 4.0 
8  b  5.0 2015 7.0 
9  c 10.0 2015 12.0 
10  d 15.0 2015 17.0 
4  e 20.0 2014 21.0 
26  f 25.0 2017 29.0 
27  g 30.0 2017 34.0 
2

last_valid_index + agg('last')

A=data.iloc[:,2:].apply(lambda x : x.last_valid_index(),1) 
B=data.groupby(['value'] * data.shape[1], 1).agg('last') 
data['date']=A 
data['obs']=B 

data 
Out[1326]: 
    field1 field2 2014 2015 2016 2017 date obs 
0  a  0.0 1.0 2.0 3.0 4.0 2017 4.0 
1  b  5.0 6.0 7.0 NaN NaN 2015 7.0 
2  c 10.0 11.0 12.0 NaN NaN 2015 12.0 
3  d 15.0 16.0 17.0 NaN NaN 2015 17.0 
4  e 20.0 21.0 NaN NaN NaN 2014 21.0 
5  f 25.0 26.0 27.0 28.0 29.0 2017 29.0 
6  g 30.0 31.0 32.0 33.0 34.0 2017 34.0 

Bằng cách sử dụng assign chúng ta có thể đẩy chúng vào một dòng như đòn

data.assign(date=data.iloc[:,2:].apply(lambda x : x.last_valid_index(),1),obs=data.groupby(['value'] * data.shape[1], 1).agg('last')) 
Out[1340]: 
    field1 field2 2014 2015 2016 2017 date obs 
0  a  0.0 1.0 2.0 3.0 4.0 2017 4.0 
1  b  5.0 6.0 7.0 NaN NaN 2015 7.0 
2  c 10.0 11.0 12.0 NaN NaN 2015 12.0 
3  d 15.0 16.0 17.0 NaN NaN 2015 17.0 
4  e 20.0 21.0 NaN NaN NaN 2014 21.0 
5  f 25.0 26.0 27.0 28.0 29.0 2017 29.0 
6  g 30.0 31.0 32.0 33.0 34.0 2017 34.0 
3

gì về apporach sau:

In [160]: df 
Out[160]: 
    field1 field2 2014 2015 2016 2017 
0  a  0.0 1.0 2.0 3.0 -10.0 
1  b  5.0 6.0 7.0 NaN NaN 
2  c 10.0 11.0 12.0 NaN NaN 
3  d 15.0 16.0 17.0 NaN NaN 
4  e 20.0 21.0 NaN NaN NaN 
5  f 25.0 26.0 27.0 28.0 29.0 
6  g 30.0 31.0 32.0 33.0 34.0 

In [180]: df.groupby(lambda x: 'obs' if x.isdigit() else x, axis=1) \ 
    ...: .last() \ 
    ...: .assign(date=df.filter(regex='^\d{4}').loc[:, ::-1].notnull().idxmax(1)) 
Out[180]: 
    field1 field2 obs date 
0  a  0.0 -10.0 2017 
1  b  5.0 7.0 2015 
2  c 10.0 12.0 2015 
3  d 15.0 17.0 2015 
4  e 20.0 21.0 2014 
5  f 25.0 29.0 2017 
6  g 30.0 34.0 2017 
+2

Tôi không chắc chắn về điều này - IIUC, OP muốn giá trị hợp lệ gần đây nhất, không phải là giá trị tối đa. Trong tập dữ liệu đã cho, chúng giống nhau, nhưng nếu (ví dụ) a cho năm 2017 là -10, tôi nghĩ đó là những gì chúng ta nên quay lại. – DSM

+0

@DSM, cảm ơn bạn đã làm rõ! Tôi nghĩ rằng nếu tôi sẽ thay thế 'max()' với 'last()' nó sẽ làm thủ thuật ... – MaxU

+1

Nhưng bây giờ bạn đang sử dụng _obs_ cuối cùng nhưng _date_ tối đa (vì vậy 2016, không phải năm 2017). [Để làm rõ, tôi có nghĩa là "ngày mà tối đa đạt được", tôi chỉ lười biếng đến mức sai.] Bạn cần tương đương với 'idxlast()' (không tồn tại, nhưng YKWIM.) – DSM

1

Ngoài ra một khả năng bằng cách sử dụng sort_valuesdrop_duplicates:

data.melt(id_vars=["field1", "field2"], var_name="date", 
      value_name="obs")\ 
    .dropna(subset=['obs'])\ 
    .sort_values(['field1', 'date'], ascending=[True, False])\ 
    .drop_duplicates('field1', keep='first') 

mang đến cho bạn

field1 field2 date obs 
21  a  0.0 2017 4.0 
8  b  5.0 2015 7.0 
9  c 10.0 2015 12.0 
10  d 15.0 2015 17.0 
4  e 20.0 2014 21.0 
26  f 25.0 2017 29.0 
27  g 30.0 2017 34.0 
+0

@ bradsolomon, cuộc sống luôn tốt hơn với các tùy chọn – DJK

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