[英]Working Around the Windows-numpy astype(int) Bug in Pandas
[英]numpy around/rint slow compared to astype(int)
所以,如果我有像x=np.random.rand(60000)*400-200
。 iPython的%timeit
說:
x.astype(int)
需要0.14ms np.rint(x)
和np.around(x)
需要1.01ms 請注意,在rint
和around
情況下,您仍然需要花費額外的0.14ms來做最終的astype(int)
(假設這是你最終想要的)。
問題:我認為大多數現代硬件都能夠在同等時間內完成兩項操作。 如果是這樣,為什么numpy需要花費8倍的時間進行舍入?
碰巧我對算術的准確性並不十分挑剔,但我看不出如何利用numpy的優勢(我正在做雜亂的生物學而不是粒子物理學)。
np.around(x).astype(int)
和x.astype(int)
不會產生相同的值。 前者舍入均勻(它與((x*x>=0+0.5) + (x*x<0-0.5)).astype(int)
)相同,而后者則向零((x*x>=0+0.5) + (x*x<0-0.5)).astype(int)
。 然而,
y = np.trunc(x).astype(int)
z = x.astype(int)
顯示y==z
但計算y
要慢得多。 所以這是np.trunc
和np.around
函數很慢。
In [165]: x.dtype
Out[165]: dtype('float64')
In [168]: y.dtype
Out[168]: dtype('int64')
所以np.trunc(x)
從double到double向零np.trunc(x)
入。 然后astype(int)
必須將double轉換為int64。
在內部我不知道python或numpy正在做什么,但我知道我將如何在C中執行此操作。讓我們討論一些硬件。 使用SSE4.1,可以使用以下方法執行從double到double的round,floor,ceil和trunc:
_mm_round_pd(a, 0); //round: round even
_mm_round_pd(a, 1); //floor: round towards minus infinity
_mm_round_pd(a, 2); //ceil: round towards positive infinity
_mm_round_pd(a, 3); //trunc: round towards zero
但numpy需要支持沒有SSE4.1的系統,所以它必須在沒有SSE4.1以及SSE4.1的情況下構建,然后使用調度程序。
但是直到使用SSE / AVX從雙直接到int64這樣做在AVX512之前效率不高。 但是,只使用SSE2可以有效地將double舍入到int32:
_mm_cvtpd_epi32(a); //round double to int32 then expand to int64
_mm_cvttpd_epi32(a); //trunc double to int32 then expand to int64
這些將兩個雙精度轉換為兩個int64。
在你的情況下,這將工作正常,因為范圍肯定在int32內。 但除非python知道范圍適合int32,否則它不能假設這樣,所以它必須舍入或截斷到int64,這是緩慢的。 此外,無論如何,numpy必須構建以支持SSE2來執行此操作。
但也許您可以使用單個浮點數組開始。 在那種情況下你可以做到:
_mm_cvtps_epi32(a); //round single to int32
_mm_cvttps_epi32(a) //trunc single to int32
這些將四個單一轉換為四個int32。
因此,為了回答您的問題,SSE2可以有效地從double舍入或截斷為int32。 使用_mm512_cvtpd_epi64(a)
或_mm512_cvttpd_epi64(a)
AVX512也能夠有效地從double到int64進行舍入或截斷。 SSE4.1可以將float / trunc / floor / ceil從float轉為float或者double或double to double。
正如@jme在注釋中指出的那樣, rint
和around
函數必須確定是否將分數向上或向下舍入到最接近的整數。 相反, astype
函數將始終向下舍入,因此它可以立即丟棄小數信息。 還有許多其他功能可以做同樣的事情。 此外,您可以通過使用較低的整數位來提高速度。 但是,您必須小心,您可以容納所有輸入數據。
%%timeit
np.int8(x)
10000 loops, best of 3: 165 µs per loop
注意,這不會存儲-128到127范圍之外的值,因為它是8位。 示例中的某些值超出此范圍。
在我試過的所有其他np.intc
, np.intc
似乎是最快的:
%%timeit
np.int16(x)
10000 loops, best of 3: 186 µs per loop
%%timeit
np.intc(x)
10000 loops, best of 3: 169 µs per loop
%%timeit
np.int0(x)
10000 loops, best of 3: 170 µs per loop
%%timeit
np.int_(x)
10000 loops, best of 3: 188 µs per loop
%%timeit
np.int32(x)
10000 loops, best of 3: 187 µs per loop
%%timeit
np.trunc(x)
1000 loops, best of 3: 940 µs per loop
你的例子,在我的機器上:
%%timeit
np.around(x)
1000 loops, best of 3: 1.48 ms per loop
%%timeit
np.rint(x)
1000 loops, best of 3: 1.49 ms per loop
%%timeit
x.astype(int)
10000 loops, best of 3: 188 µs per loop
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.