使用np.int16、np.int32时,pandas math arithmetic's、pandas.eval()中未报告整数溢出

hrirmatl  于 2023-06-20  发布在  其他
关注(0)|答案(1)|浏览(106)

我需要处理相当大的数据框架~80M条记录,本质上内存消耗是一个问题。因此,包含数值数据的列将收缩到可能的最小dtype,如np.int8,np.int16,np.int32。在某个阶段,我需要使用现有列的一些数学计算新列,它需要int64的容量。大多数Pandas算术构造失败的地方。我花了一段时间才发现原因是整数溢出:简而言之,计算

newCol = col16*col16, 
   newCol = col32*value16,

尽管newCol创建为int64,但通常会产生不正确的结果。以下是一些简单显式示例:计算newCol = A * 100000,显然对于任何A=aaaaa都应该计算出类似aaaaa00000的值。
但是,请参见下文:

import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(1<<7,1<<15, size=(int(5))), columns=list('A'), dtype=np.int16)
df.eval('Q = A * 100000', inplace=True) # 1st naive approach from a head
df['W'] = df['A'] * 100000

# trying to use const c=int64() to force expr evaluator to use int64
c = np.int64(10000)
df.eval('R = @c * A', inplace=True)     

# trying to create new int64 col 1st and use it in calc:
df['T']=0    # this creates new col 'T' dtype=int64 filled with 0
df.eval('T = 100000 * A', inplace=True)

df['S']=0    
# trying to force int64  via 1st element 'S', which is int64
df['S'] = df['S'] + df['A'] * 100000 

# here finally this approach works, calculation is using int64 instructions:
df['X']=1   
df.eval('X = X * 100000 * A', inplace=True)

# just preformatting
pd.set_option('display.max_columns', None)
pd.options.display.width=222
df.index=[''] * len(df)

print(df)
df.info()

A           Q           W           R           T           S           X
   3396   339600000   339600000   339600000   339600000   339600000   339600000
  26508 -1644167296 -1644167296 -1644167296 -1644167296 -1644167296  2650800000
  27942 -1500767296 -1500767296 -1500767296 -1500767296 -1500767296  2794200000
   3441   344100000   344100000   344100000   344100000   344100000   344100000
  27880 -1506967296 -1506967296 -1506967296 -1506967296 -1506967296  2788000000
<class 'pandas.core.frame.DataFrame'>
Index: 5 entries,  to 
Data columns (total 7 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   A       5 non-null      int16
 1   Q       5 non-null      int32
 2   W       5 non-null      int32
 3   R       5 non-null      int32
 4   T       5 non-null      int64
 5   S       5 non-null      int64
 6   X       5 non-null      int64
dtypes: int16(1), int32(3), int64(3)
memory usage: 230.0+ bytes

用6种不同的方法来完成琐碎的数学运算,只有col 'X'产生了我(我猜大多数用户)所期望的。
一个明显的解决方法是,首先将源列的dtype转换为int64(就像“在飞行中”一样),如

df['x'] = df['A'].astype(np.int64) * 100000

但我不认为这是一个很好的解决方案在我的情况下,因为数据已经是大到创建一个更大的tmp副本,性能也将下降冗余转换。
所以我的问题是如何在运行中计算它,而不是在int64中创建整个源数据列的副本(没有RAM),例如在64位CPU中直接正确计算newCol64 = srcCol8 * srcCol16 * srcCol16
pandas.eval()是否存在显式类型转换语法?在逐行计算结果时可以动态地完成哪些操作?

ogsagwnx

ogsagwnx1#

如果将数组乘以标量值,则选定的dtype将是该数组的dtype。然而,如果你在两个数组之间做一个数学运算,比如X(X * A),数组将被“向上转换”:

>>> df['A'] * c
0   -31392
1    14992
2   -14064
3     6944
4   -13904
Name: A, dtype: int16  # bad result, bad dtype

>>> df['A'] * [c]
0    196380000
1    288570000
2    317770000
3    168500000
4     76270000
Name: A, dtype: int64  # everything is ok

您可以阅读有关输出类型确定的文档。
在您的情况下,您可以:

c = np.array([10000])
df.eval('R = @c * A', inplace=True)

输出:

>>> df
       A          R
0  12399  123990000
1   6026   60260000
2  17133  171330000
3  30974  309740000
4  28216  282160000

>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   A       5 non-null      int16
 1   R       5 non-null      int64
dtypes: int16(1), int64(1)
memory usage: 178.0 bytes

相关问题