2016-12-21 61 views
8

更有效的方式我有一個數據幀df具有列['metric_type', 'metric_value']。對於每一行,我要確保我有名字等於'metric_type',併爲該列等於'metric_value'值的列。清潔字符串列,並添加一個新列

我的一個問題是,'metric_type'有,我想擺脫虛假的空間。

考慮數據框df

df = pd.DataFrame([ 
     ['a ', 1], 
     [' b', 2], 
     [' c ', 3] 
    ], columns=['metric_type', 'metric_value']) 

print(df) 

    metric_type metric_value 
0   a    1 
1   b    2 
2   c    3 

注意的'metric_type'每個數據都在不同地方的空間。

我創建了一個功能使用apply,但它需要一個可怕的很長一段時間。

def assign_metric_vals(row): 
    row[row['metric_type'].replace(" ", "")] = row['metric_value'] 
    return row 

當我使用它,我得到這個:

 a b c metric_type metric_value 
0 1.0000 nan nan   a    1 
1 nan 2.00 nan   b    2 
2 nan nan 3.00   c    3 

有沒有更好的(閱讀, 「更快」)的方式來完成這個相同的任務?

+2

'apply'本質上是緩慢的。它基本上是一個python for循環的包裝器。 –

+0

無論如何,似乎無論你想完成什麼,都應該可以通過基本的「熊貓」任務完成。你爲什麼不描述你想要完成的事情 –

+0

如果那麼簡單,我不會發布這個問題。如果在我正在努力完成的事情中存在誤解,請抱歉。有一個列metric_type包含我轉換爲列的值。然後,我需要將metric_value列的行分配給適當的轉換的metric_type列。 – user3002486

回答

11

你好得多送達metric_type和拆垛設置索引。

df.set_index(df.metric_type.str.replace(' ', ''), append=True).metric_value.unstack() 

示範

df = pd.DataFrame([ 
     ['a ', 1], 
     [' b', 2], 
     [' c ', 3] 
    ], columns=['metric_type', 'metric_value']) 

print(df) 

    metric_type metric_value 
0   a    1 
1   b    2 
2   c    3 

print(df.apply(assign_metric_vals, 1)) 

     a b c metric_type metric_value 
0 1.0000 nan nan   a    1 
1 nan 2.00 nan   b    2 
2 nan nan 3.00   c    3 

或我的方式

idx = df.metric_type.str.replace(' ', '') 
d1 = df.set_index(idx, append=True).metric_value.unstack() 
print(pd.concat([d1, df], axis=1)) 

     a b c metric_type metric_value 
0 1.0000 nan nan   a    1 
1 nan 2.00 nan   b    2 
2 nan nan 3.00   c    3 

定時

使用一個更大的df
df1 = pd.concat([df] * 30000, ignore_index=True)

%%timeit 
idx = df1.metric_type.str.replace(' ', '') 
d1 = df1.set_index(idx, append=True).metric_value.unstack() 
pd.concat([d1, df1], axis=1) 

10個循環,最好的3:每次循環

%%timeit 
df1.apply(assign_metric_vals, 1) 
.3毫秒

1循環,最好的3:每循環

+0

美麗的代碼,謝謝 – user3002486

+0

歡迎您 – piRSquared

+0

NP,投票在我的新問題和答案。我從電話應用發佈了這個問題。 – piRSquared

2

57.4這兒是即約20%的速度,並給出了相同的答案@ piRSquared的一種替代。我不認爲這是無論是好還是壞(一般),但賞金被張貼後,這個問題的答案被接受,所以我會提供這個作爲一個額外的選擇。

%%timeit 
idx = df1.metric_type.str.replace(' ', '') 
d1 = df1.set_index(idx, append=True).metric_value.unstack() 
result1 = pd.concat([d1, df1], axis=1) 
10 loops, best of 3: 97.6 ms per loop 

%%timeit 
df1.metric_type = df1.metric_type.str.strip() 
d1 = df1.pivot(columns='metric_type', values='metric_value') 
result2 = pd.concat([d1, df1], axis=1) 
10 loops, best of 3: 77.2 ms per loop 

約1/3的速度的提高是使用strip代替replace和2/3使用pivot代替unstack。 (無論如何,concat步驟是相同的​​且非常快)。

2

綜觀其最終的數據框被創建的方式,一熱編碼字符串列似乎並不像一個壞主意確實方面的它相比,到目前爲止提到的其他方法的整體性能。

步驟:

  1. metric_type系列使用pd.get_dummies,創建分類那些虛擬變量。這部分加上str.strip是該地段最耗時的。

  2. 相反剝離的領先/直接串聯對象的尾部空格的,我們可以用計算get_dummies部分玩完,因爲有較高的機會,一些分類變量確實會重複在一系列以後會分享虛擬創作過程中的同一列。重複的變量越多,過濾掉這些額外空間的時間就越少。僅在虛擬變量DF的獲取列上執行str.strip。這種方法是一個巨大的節省時間。獲得

  3. 排序這些列,使得它們按字典順序排序,並且所複製的那些(如果存在)將被置於鄰近於彼此。允許DF根據這些列的組合進行修改。
  4. 利用np.uniquereturn_index=True參數提取唯一列存在,並且它的相應指標。
  5. 我們需要找到一種方法將相同的列組合成一個有益健康的列。對於這一點,我們可以使用np.add.reduceat其工作原理類似於groupby操作(相當於 - df.groupby(df.columns.tolist(), axis=1).sum()),但它在是非常快的特產。待配對通過np.unique值的.The還原idx供給的索引出現在這些片和它們的運行總和被跨列(axis=1)來計算。
  6. 返回的dtypebool以幫助我們使用np.where,因爲它的功能就像一個布爾面罩,其中1的/ 0的被映射到分別True/False。然後這些1被填充在metric_value系列中的值和0的NaN
  7. 我們DF現在準備它需要與獲得最終的清理數據框原開始DF縱列級聯。

解決方案:

def dummies_strip_concat(df): 
    one_hot_enc = pd.get_dummies(df.metric_type) 
    one_hot_enc.columns = one_hot_enc.columns.str.strip() 
    one_hot_enc.sortlevel(axis=1, inplace=True) 
    a, idx = np.unique(one_hot_enc.columns.values, return_index=True) 
    out = np.where(np.add.reduceat(one_hot_enc.values, idx, axis=1, dtype=np.bool), 
        df.metric_value.values[:, None], 
        np.nan) 
    return (pd.concat([pd.DataFrame(out, df.index, a), df], axis=1)) 

時序:

def pir(df): 
    idx = df.metric_type.str.replace(' ', '') 
    d1 = df.set_index(idx, append=True).metric_value.unstack() 
    return pd.concat([d1, df], axis=1) 

def johne(df): 
    df.metric_type = df.metric_type.str.strip() 
    d1 = df.pivot(columns='metric_type', values='metric_value') 
    return pd.concat([d1, df], axis=1) 

對於含有可比什麼OP心中有幾千行的DF

df1 = pd.concat([df] * 30000, ignore_index=True) 
df1.shape 
(90000, 2) 

# Check whether they produce the same outcome 
dummies_strip_concat(df1).equals(pir(df1)) 
True 

%timeit pir(df1) 
10 loops, best of 3: 97.5 ms per loop 

%timeit johne(df1) 
10 loops, best of 3: 76.5 ms per loop 

%timeit dummies_strip_concat(df1) 
100 loops, best of 3: 13.2 ms per loop 
+0

非常好的解決方案。 – piRSquared

+0

謝謝!花了我一段時間才達到此目的。也許你可以想出一些你剛剛學過的'numpy'技術,並且更好。祝你有美好的一年! –

+1

我只是喜歡讓人們參與和學習其他技術。祝你新年快樂。 – piRSquared

相關問題