2017-10-16 22 views
2

我對Python很新,所以我認爲這可能是一個基本問題。我在網上找到了一些解決方案,但無法找到我正在尋找的確切的東西。目前我正在尋找一種方法來查找超過3列數據的「低中位數」。如果只有2列的3列填充,那麼我想採取較低的值。高效地找到多個列的低中位數

以下是我發現至今

df['median']=np.nanmedian(df[['val1','val2','val3']], axis=1) 

以上不是一個可行的解決方案,因爲我沒有看到一個參數的任何信息,採取低中位時有偶數個值。此外,我發現有一個功能,做什麼我尋找

statistics.median_low() 

不過,我不確定如何在多個列應用它,而無需使用某種功能,計算在每個中間行明智的時間(即循環或應用函數)。理想情況下,我想要一個使用此函數的矢量化解決方案,它將同時計算中位數。謝謝您的幫助。

+0

如果它的三列(奇數),爲什麼你需要擔心低? – Divakar

+0

有時候有NULL值 –

+0

NULL,你的意思是NaNs,對吧? – Divakar

回答

2

一些優化是可能的3個柱數據進行排序每一行的使用,然後簡單地選擇第一或第二列基於NaNs,由於被排序會被推到每行的末尾。這使我們可以使用slicing進行選擇並獲得每行所需的median_low值。

這裏的那些組裝成一個量化的解決方案 -

a = df.values 
a_sorted = np.sort(a,1) 
df['median'] = np.where(np.isnan(a_sorted[:,2]), a_sorted[:,0], a_sorted[:,1]) 

運行測試

途徑 -

# Proposed in this post 
def vectorized_app(df): 
    a = df.values 
    a_sorted = np.sort(a,1) 
    df['median'] = np.where(np.isnan(a_sorted[:,2]), a_sorted[:,0], a_sorted[:,1]) 
    return df 

# @piRSquared's new soln 
def vectorized_app2(df): 
    v = np.sort(df.values, axis=1) 
    n = np.count_nonzero(~np.isnan(v), axis=1) 
    j = (n - 1) // 2 
    i = np.arange(len(v)) 
    return df.assign(median_low=v[i, j]) 

# @piRSquared's old soln 
from statistics import median_low 
def apply_app(df): 
    med = lambda x: median_low(x.dropna()) 
    return df.apply(med, 1) 

計時 -

In [433]: # Setup input dataframe and set one per row as NaN 
    ...: np.random.seed(0) 
    ...: a = np.random.randint(0,9,(10000,3)).astype(float) 
    ...: idx = np.random.randint(0,3,a.shape[0]) 
    ...: a[np.arange(a.shape[0]), idx] = np.nan 
    ...: df = pd.DataFrame(a) 
    ...: df.columns = [['val1','val2','val3']] 
    ...: 

In [435]: %timeit vectorized_app(df) 
1000 loops, best of 3: 481 µs per loop 

In [436]: %timeit vectorized_app2(df) 
1000 loops, best of 3: 892 µs per loop 

In [434]: %timeit apply_app(df) 
1 loop, best of 3: 1.15 s per loop 
+1

謝謝!將於今天晚些時候進行測試並報告結果。 –

+0

我發佈了一個新的解決方案。 – piRSquared

+2

@piRSquared更新時間。 – Divakar

2

答案
這是一個廣義的解決方案,適用於任何大小的數組。

我對每一行進行排序,計算有多少個非空值,然後確定median_low必須位於何處。

v = np.sort(df.values, axis=1) 
n = np.count_nonzero(~np.isnan(v), axis=1) 
j = (n - 1) // 2 
i = np.arange(len(v)) 

df.assign(median_low=v[i, j]) 

    A B C median_low 
0 4 5.0 8.0   5.0 
1 3 6.0 4.0   4.0 
2 4 9.0 NaN   4.0 
3 1 NaN NaN   1.0 

老回答

首先,你需要使用pd.DataFrame.applyaxis=1選項功能應用到每一行。

其次,median_low將認爲是空值。你不希望出現這種情況,所以做一個拉姆達砸空,然後使用median_low


import pandas as pd 
from statistics import median_low 

df = pd.DataFrame([ 
    [4, 5, 8], 
    [3, 6, 4], 
    [4, 9], 
    [1] 
], columns=list('ABC')) 

med = lambda x: median_low(x.dropna()) 

df.apply(med, 1) 

0 5.0 
1 4.0 
2 4.0 
3 1.0 
dtype: float64 
+0

非常感謝您的幫助,這非常明確,很有道理。唯一的是我需要一個矢量化的解決方案。我的理解是,當使用apply over rows時,它的功能與循環類似,一次計算一個值。 –

+1

當我回答這些問題。我通常會發布第一件能發揮作用的東西。然後我探索一下,看看能否提出更好的答案。到目前爲止,你有一些工作。我們會看到我還能想出什麼,或者別人能想出什麼。 – piRSquared

相關問題