![](/img/trans.png)
[英]Find the column name of the second largest value of each row in a Pandas DataFrame
[英]How to get column name for second largest row value in pandas DataFrame
我有一個非常簡單的問題 - 我認為 - 但似乎我無法繞過這個問題。 我是Python和Pandas的初學者。 我搜索了論壇,但無法獲得符合我需要的(最近)答案。
我有一個這樣的數據框:
df = pd.DataFrame({'A': [1.1, 2.7, 5.3], 'B': [2, 10, 9], 'C': [3.3, 5.4, 1.5], 'D': [4, 7, 15]}, index = ['a1', 'a2', 'a3'])
這使:
A B C D
a1 1.1 2 3.3 4
a2 2.7 10 5.4 7
a3 5.3 9 1.5 15
我的問題很簡單:我想添加一列,給出每行第二個最大值的列名。
我寫了一個簡單的函數,它返回每行的第二個最大值
def get_second_best(x):
return sorted(x)[-2]
df['value'] = df.apply(lambda row: get_second_best(row), axis=1)
這使:
A B C D value
a1 1.1 2 3.3 4 3.3
a2 2.7 10 5.4 7 7.0
a3 5.3 9 1.5 15 9.0
但是我找不到如何在'value'列中顯示列名而不是值...我正在考慮布爾索引(比較'value'列值與每行),但我沒有'我想出了怎么做。
為了更清楚,我希望它是:
A B C D value
a1 1.1 2 3.3 4 C
a2 2.7 10 5.4 7 D
a3 5.3 9 1.5 15 B
任何幫助(和解釋)贊賞!
一種方法是使用Series.nlargest
每行中最大的兩個元素,並找到與使用Series.idxmin
的最小元素對應的列:
In [45]: df['value'] = df.T.apply(lambda x: x.nlargest(2).idxmin())
In [46]: df
Out[46]:
A B C D value
a1 1.1 2 3.3 4 C
a2 2.7 10 5.4 7 D
a3 5.3 9 1.5 15 B
值得注意的是,在Series.idxmin
上選擇DataFrame.idxmin
可以在性能方面有所不同:
df = pd.DataFrame(np.random.normal(size=(100, 4)), columns=['A', 'B', 'C', 'D'])
%timeit df.T.apply(lambda x: x.nlargest(2).idxmin()) # 39.8 ms ± 2.66 ms
%timeit df.T.apply(lambda x: x.nlargest(2)).idxmin() # 53.6 ms ± 362 µs
編輯:添加到@jpp的答案,如果性能很重要,你可以通過使用Numba獲得顯着的加速,編寫代碼就好像這是C並編譯它:
from numba import njit, prange
@njit
def arg_second_largest(arr):
args = np.empty(len(arr), dtype=np.int_)
for k in range(len(arr)):
a = arr[k]
second = np.NINF
arg_second = 0
first = np.NINF
arg_first = 0
for i in range(len(a)):
x = a[i]
if x >= first:
second = first
first = x
arg_second = arg_first
arg_first = i
elif x >= second:
second = x
arg_second = i
args[k] = arg_second
return args
讓我們分別比較兩組數據的不同解決方案:形狀(1000, 4)
和(1000, 1000)
:
df = pd.DataFrame(np.random.normal(size=(1000, 4)))
%timeit df.T.apply(lambda x: x.nlargest(2).idxmin()) # 429 ms ± 5.1 ms
%timeit df.columns[df.values.argsort(1)[:, -2]] # 94.7 µs ± 2.15 µs
%timeit df.columns[np.argpartition(df.values, -2)[:,-2]] # 101 µs ± 1.07 µs
%timeit df.columns[arg_second_largest(df.values)] # 74.1 µs ± 775 ns
df = pd.DataFrame(np.random.normal(size=(1000, 1000)))
%timeit df.T.apply(lambda x: x.nlargest(2).idxmin()) # 1.8 s ± 49.7 ms
%timeit df.columns[df.values.argsort(1)[:, -2]] # 52.1 ms ± 1.44 ms
%timeit df.columns[np.argpartition(df.values, -2)[:,-2]] # 14.6 ms ± 145 µs
%timeit df.columns[arg_second_largest(df.values)] # 1.11 ms ± 22.6 µs
在最后一種情況下,通過使用@njit(parallel=True)
並將外部循環替換for k in prange(len(arr))
,我能夠擠出更多並將基准測試降低到@njit(parallel=True)
。
這是使用NumPy的一個解決方案。 我們的想法是對數據argsort
的值進行argsort
,選擇倒數第二列,最后使用它來索引df.column
。
df['value'] = df.columns[df.values.argsort(1)[:, -2]]
print(df)
A B C D value
a1 1.1 2 3.3 4 C
a2 2.7 10 5.4 7 D
a3 5.3 9 1.5 15 B
您應該發現這比基於Pandas的解決方案更有效:
# Python 3.6, NumPy 1.14.3, Pandas 0.23.0
np.random.seed(0)
df = pd.DataFrame(np.random.normal(size=(100, 4)), columns=['A', 'B', 'C', 'D'])
%timeit df.T.apply(lambda x: x.nlargest(2).idxmin()) # 49.6 ms
%timeit df.T.apply(lambda x: x.nlargest(2)).idxmin() # 73.2 ms
%timeit df.columns[df.values.argsort(1)[:, -2]] # 36.3 µs
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.