Cách nhanh nhất (trong giới hạn của tính linh động sane) là gì để đếm các giá trị riêng biệt, trên các cột của cùng một dtype
, cho mỗi hàng trong một DataFrame
?tính hiệu quả riêng biệt trên các cột của DataFrame, được nhóm theo hàng
Chi tiết: Tôi có DataFrame
kết quả phân loại theo chủ đề (theo hàng) theo ngày (trong cột), tương tự với nội dung được tạo sau đây.
import numpy as np
import pandas as pd
def genSampleData(custCount, dayCount, discreteChoices):
"""generate example dataset"""
np.random.seed(123)
return pd.concat([
pd.DataFrame({'custId':np.array(range(1,int(custCount)+1))}),
pd.DataFrame(
columns = np.array(['day%d' % x for x in range(1,int(dayCount)+1)]),
data = np.random.choice(a=np.array(discreteChoices),
size=(int(custCount), int(dayCount)))
)], axis=1)
Ví dụ, nếu tập dữ liệu cho biết mỗi thức uống mà mỗi khách hàng đặt hàng trên mỗi lần đến cửa hàng, tôi muốn biết số lượng đồ uống riêng biệt cho mỗi khách hàng.
# notional discrete choice outcome
drinkOptions, drinkIndex = np.unique(['coffee','tea','juice','soda','water'],
return_inverse=True)
# integer-coded discrete choice outcomes
d = genSampleData(2,3, drinkIndex)
d
# custId day1 day2 day3
#0 1 1 4 1
#1 2 3 2 1
# Count distinct choices per subject -- this is what I want to do efficiently on larger DF
d.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1)
#0 2
#1 3
# Note: I have coded the choices as `int` rather than `str` to speed up comparisons.
# To reconstruct the choice names, we could do:
# d.iloc[:,1:] = drinkOptions[d.iloc[:,1:]]
Những gì tôi đã cố gắng: Các bộ dữ liệu trong trường hợp sử dụng này sẽ có nhiều đối tượng hơn ngày (ví dụ testDf
dưới đây), vì vậy tôi đã cố gắng tìm ra hàng khôn ngoan hoạt động hiệu quả nhất:
testDf = genSampleData(100000,3, drinkIndex)
#---- Original attempts ----
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: x.nunique(), axis=1)
# I didn't wait for this to finish -- something more than 5 seconds per loop
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(x.unique()), axis=1)
# Also too slow
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1)
#20 loops, best of 3: 2.07 s per loop
Để cải thiện về nỗ lực ban đầu của tôi, chúng tôi lưu ý rằng pandas.DataFrame.apply() chấp nhận lập luận:
Nếu
raw=True
p Hàm assed sẽ nhận các đối tượng ndarray thay thế. Nếu bạn chỉ áp dụng một hàm giảm NumPy này sẽ đạt được hiệu suất tốt hơn
này đã cắt giảm thời gian chạy bằng hơn một nửa:
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1, raw=True)
#20 loops, best of 3: 721 ms per loop *best so far*
Tôi rất ngạc nhiên rằng một giải pháp NumPy tinh khiết, mà dường như tương đương với ở trên với raw=True
, thực sự chậm hơn một chút:
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=1, arr = testDf.iloc[:,1:].values)
#20 loops, best of 3: 1.04 s per loop
Cuối cùng, tôi cũng đã thử transpo hát dữ liệu để làm column-wise count distinct, mà tôi nghĩ có thể hiệu quả hơn (ít nhất là cho DataFrame.apply()
, nhưng dường như không có sự khác biệt có ý nghĩa.
%timeit -n20 testDf.iloc[:,1:].T.apply(lambda x: len(np.unique(x)), raw=True)
#20 loops, best of 3: 712 ms per loop *best so far*
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=0, arr = testDf.iloc[:,1:].values.T)
# 20 loops, best of 3: 1.13 s per loop
Cho đến nay giải pháp tốt nhất của tôi là một sự pha trộn kỳ lạ của df.apply
của len(np.unique())
, nhưng những gì khác nên tôi cố gắng?
Là đại diện ngày? Nó dường như ảnh hưởng đến sự khác biệt trong các buổi biểu diễn rất nhiều. – ayhan
@ayhan thú vị ... số ngày là đại diện cho trường hợp sử dụng cụ thể của tôi nhưng nếu một cái gì đó khác hoạt động tốt hơn cho bộ dữ liệu rộng hơn sẽ đáng chú ý cho người dùng khác – C8H10N4O2
Điều ngược lại thực sự. Có vẻ như so sánh từng cột với những người khác nhanh hơn nhiều khi bạn có một số lượng nhỏ các cột. Tôi đã đăng kết quả dưới dạng câu trả lời. – ayhan