[英]How to efficiently convert Pandas Dataframe into a multidimensional numpy array?
[英]How to efficiently iterate a pandas DataFrame and increment a NumPy array on these values?
我的熊貓/ numpy生銹了,我寫的代碼感覺效率低下。
我正在Python3.x初始化一個numpy零的數組,長度為1000.為了我的目的,這些只是整數:
import numpy as np
array_of_zeros = np.zeros((1000, ), )
我還有以下DataFrame(比我的實際數據小得多)
import pandas as pd
dict1 = {'start' : [100, 200, 300], 'end':[400, 500, 600]}
df = pd.DataFrame(dict1)
print(df)
##
## start end
## 0 100 400
## 1 200 500
## 2 300 600
DataFrame有兩列, start
和end
。 這些值表示一系列值,即start
始終是小於end
整數。 在上面,我們看到第一行的范圍是100-400
,接下來是200-500
,然后是300-600
。
我的目標是逐行遍歷pandas DataFrame,並根據這些索引位置遞增numpy數組array_of_zeros
。 因此,如果數據幀中有一行為10
到20
,我想將索引10-20的零增加+1。
這是我想要的代碼:
import numpy as np
array_of_zeros = np.zeros((1000, ), )
import pandas as pd
dict1 = {'start' : [100, 200, 300], 'end':[400, 500, 600]}
df = pd.DataFrame(dict1)
print(df)
for idx, row in df.iterrows():
for i in range(int(row.start), int(row.end)+1):
array_of_zeros[i]+=1
它的工作原理!
print(array_of_zeros[15])
## output: 0.0
print(array_of_zeros[600])
## output: 1.0
print(array_of_zeros[400])
## output: 3.0
print(array_of_zeros[100])
## output: 1.0
print(array_of_zeros[200])
## output: 2.0
我的問題:這是非常笨拙的代碼! 我不應該使用那么多帶有numpy數組的for循環! 如果輸入數據幀非常大,則此解決方案效率非常低
是否有更有效(即更多基於numpy)的方法來避免這種for循環?
for i in range(int(row.start), int(row.end)+1):
array_of_zeros[i]+=1
也許有一個以熊貓為導向的解決方案?
numpy.bincount
np.bincount(np.concatenate(
[np.arange(a, b + 1) for a, b in zip(df.start, df.end)]
), minlength=1000)
numpy.add.at
a = np.zeros((1000,), np.int64)
for b, c in zip(df.start, df.end):
np.add.at(a, np.arange(b, c + 1), 1)
您可以使用NumPy數組索引來避免內部循環,即res[np.arange(A[i][0], A[i][1]+1)] += 1
,但這不是有效的,因為它涉及創建一個新數組並使用高級索引。
相反,您可以使用numba
1來優化您的算法,就像它的情況一樣。 下面的示例通過將性能關鍵邏輯移動到JIT編譯的代碼來顯示出巨大的性能提升。
from numba import jit
@jit(nopython=True)
def jpp(A):
res = np.zeros(1000)
for i in range(A.shape[0]):
for j in range(A[i][0], A[i][1]+1):
res[j] += 1
return res
一些基准測試結果:
# Python 3.6.0, NumPy 1.11.3
# check result the same
assert (jpp(df[['start', 'end']].values) == original(df)).all()
assert (pir(df) == original(df)).all()
assert (pir2(df) == original(df)).all()
# time results
df = pd.concat([df]*10000)
%timeit jpp(df[['start', 'end']].values) # 64.6 µs per loop
%timeit original(df) # 8.25 s per loop
%timeit pir(df) # 208 ms per loop
%timeit pir2(df) # 1.43 s per loop
用於基准測試的代碼:
def original(df):
array_of_zeros = np.zeros(1000)
for idx, row in df.iterrows():
for i in range(int(row.start), int(row.end)+1):
array_of_zeros[i]+=1
return array_of_zeros
def pir(df):
return np.bincount(np.concatenate([np.arange(a, b + 1) for a, b in \
zip(df.start, df.end)]), minlength=1000)
def pir2(df):
a = np.zeros((1000,), np.int64)
for b, c in zip(df.start, df.end):
np.add.at(a, np.arange(b, c + 1), 1)
return a
1對於后人,我包括@ piRSquared關於為什么numba
在這里幫助的優秀評論:
numba
的優勢在於非常有效地循環。 雖然它可以理解NumPy的大部分API,但通常最好避免在循環中創建NumPy對象。 我的代碼是為數據幀中的每一行創建一個NumPy數組。 然后在使用bincount之前連接它們。 @jpp的numba
代碼創建了很少的額外對象,並利用了已有的大部分內容。 我的NumPy解決方案和@jpp的numba
解決方案之間的差異大約是4-5倍。 兩者都是線性的,應該很快。
我的解決方案
for x, y in zip(df.start, df.end):
array_of_zeros[x:y+1]+=1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.