2017-10-20 175 views
0

我有一個相當具體的算法,我想遵循。根據行和列條件設置熊貓數據幀值

基本上我有一個數據幀如下:

 month taken score 
1  1  2  23 
2  1  1  34 
3  1  2  12 
4  1  2  59 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2  45 
9  3  1  43 
10  3  2  43 
11  4  1  23 
12  4  2  94 

我想讓它使「分數」一欄更改爲100,其中採取== 2持續,直到月底天。所以,沒有采取== 2中所有出現有其得分爲100,如果在一個月之後的任何一天都有一個採取== 1

所以結果我想要的是:

 month taken score 
1  1  2  23 
2  1  1  34 
3  1  2  100 
4  1  2  100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2  100 
9  3  1  43 
10  3  2  43 
11  3  1  23 
12  3  2  100 
13  4  1  32 
14  4  2  100 

我寫這個代碼,我覺得應該這樣做:

#iterate through months 
for month in range(12): 
    #iterate through scores 
    for score in range(len(df_report.loc[df_report['month'] == month+1])): 
     #starting from the bottom, of that month, if 'taken' == 2... 
     if df_report.loc[df_report.month==month+1, 'taken'].iloc[-score-1] == 2: 
      #then set the score to 100 
      df_report.loc[df_report.month==month+1, 'score'].iloc[-score-2] = 100 
     #if you run into a 'taken' == 1, move on to next month 
     else: break 

然而,這似乎並沒有更改任何值,儘管不引發錯誤...它也沒有給我一個錯誤關於將值設置爲複製的數據幀。

任何人都可以解釋我做錯了什麼嗎?

+0

如果我猜的話,這將是你用新值設置副本。鏈接* loc呼叫不是最好的主意。 –

+0

我認爲你是對的,但我該如何解決這個問題?另外,如果.loc不是副本,並且.iloc不是副本,那麼爲什麼.loc的.iloc是副本?! – James

回答

2

原因你的價值觀不被更新是分配給iloc更新副本由前loc調用返回,使原來沒有被觸及。


下面是我如何解決這個問題。首先,定義一個函數foo

def foo(df): 
    for i in reversed(df.index): 
     if df.loc[i, 'taken'] != 2: 
      break 
     df.loc[i, 'score'] = 100 
     i -= 1 
    return df 

現在,groupbymonth並調用foo

df = df.groupby('month').apply(foo) 
print(df) 
    month taken score 
1  1  2  23 
2  1  1  34 
3  1  2 100 
4  1  2 100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2 100 
9  3  1  43 
10  3  2 100 
11  4  1  23 
12  4  2 100 

顯然,apply有它的缺點,但我想不出一個向量化的辦法處理這一問題。

+0

我也沒有。我可以擺脫for循環,但不適用於groupby – Dark

+0

謝謝,這完美的作品 – James

2

你可以做

import numpy as np 
def get_value(x): 
    s = x['taken'] 
    # Get a mask of duplicate sequeence and change values using np.where 
    mask = s.ne(s.shift()).cumsum().duplicated(keep=False) 
    news = np.where(mask,100,x['score']) 

    # if last number is 2 then change the news value to 100 
    if s[s.idxmax()] == 2: news[-1] = 100 
    return pd.Series(news) 

df['score'] = df.groupby('month').apply(get_value).values 

輸出:

 
    month taken score 
1  1  2  23 
2  1  1  34 
3  1  2 100 
4  1  2 100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2 100 
9  3  1  43 
10  3  2 100 
11  4  1  23 
12  4  2 100 

幾乎相同的速度,但@coldspeed是贏家

ndf = pd.concat([df]*10000).reset_index(drop=True) 

%%timeit 
ndf['score'] = ndf.groupby('month').apply(foo) 
10 loops, best of 3: 40.8 ms per loop 


%%timeit 
ndf['score'] = ndf.groupby('month').apply(get_value).values 
10 loops, best of 3: 42.6 ms per loop 
+1

當然,這比簡單的迭代更好? –

+0

我不知道需要檢查速度 – Dark

+0

@cᴏʟᴅsᴘᴇᴇᴅ它非常非常奇怪。無論數據幀在我的電腦中有多大,差值都是2毫秒。 – Dark