Python 加快大型阵列上的操作&;数据集(速度慢,质量好,进一步改进?)
我有一个由数百万行和大约6列组成的大型数据集。数据目前在熊猫数据框中,我正在寻找最快的操作方法。例如,假设我要删除一列中的值为“1”的所有行 以下是我的最小工作示例:Python 加快大型阵列上的操作&;数据集(速度慢,质量好,进一步改进?),python,pandas,performance,numpy,optimization,Python,Pandas,Performance,Numpy,Optimization,我有一个由数百万行和大约6列组成的大型数据集。数据目前在熊猫数据框中,我正在寻找最快的操作方法。例如,假设我要删除一列中的值为“1”的所有行 以下是我的最小工作示例: # Create dummy data arrays and pandas dataframe array_size = int(5e6) array1 = np.random.rand(array_size) array2 = np.random.rand(array_size) array3 = np.random.rand(
# Create dummy data arrays and pandas dataframe
array_size = int(5e6)
array1 = np.random.rand(array_size)
array2 = np.random.rand(array_size)
array3 = np.random.rand(array_size)
array_condition = np.random.randint(0, 3, size=array_size)
df = pd.DataFrame({'array_condition': array_condition, 'array1': array1, 'array2': array2, 'array3': array3})
def method1():
df_new = df.drop(df[df.array_condition == 1].index)
编辑:正如Henry Yik在评论中指出的,更快的方法是:
def method1b():
df_new = df[df.array_condition != 1]
我相信Pandas在这方面会非常慢,所以我还使用numpy实现了一个方法,将每个列作为一个单独的数组进行处理:
def method2():
masking = array_condition != 1
array1_new = array1[masking]
array2_new = array2[masking]
array3_new = array3[masking]
array_condition_new = array_condition[masking]
结果是:
%timeit method1()
625 ms ± 7.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit methodb()
158 ms ± 7.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit method2()
138 ms ± 3.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
因此,我们确实看到了使用numpy时性能的轻微提升。然而,这是以可读性差得多的代码为代价的(即必须创建掩码并将其应用于每个数组)。这个方法看起来也没有可伸缩性,因为如果我有,比如说,30列数据,我将需要很多行代码将掩码应用到每个数组!此外,允许可选列也很有用,因此此方法可能无法对空数组进行操作
因此,我有两个问题:
1) 在numpy中是否有更干净/更灵活的方法来实现这一点
2) 或者更好,这里有没有更高性能的方法?e、 吉特(麻木?),赛顿还是别的什么
另外,在实践中,可以使用就地操作,在删除数据后用新数组替换旧数组您可能会发现使用此操作很有用。它将布尔掩码转换为数组索引,从而降低了使用成本。将其与numpy.vstack结合使用可以实现一些内存廉价的操作:
def method3():
wh = np.where(array_condition == 1)
return np.vstack(tuple(col[wh] for col in (array1, array2, array3)))
这将提供以下时间:
>>> %timeit method2()
180 ms ± 6.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit method3()
96.9 ms ± 2.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
元组解包允许操作在内存中相当轻松,因为当对象重新绑定在一起时,它会更小。如果需要直接从数据帧中取出列,以下代码段可能会很有用:
def method3b():
wh = np.where(array_condition == 1)
col_names = ['array1','array2','array3']
return np.vstack(tuple(col[wh] for col in tuple(df[col_name].to_numpy()
for col_name in col_names)))
这允许从数据帧中按名称获取列,然后动态地对这些列进行元组解压缩。速度大致相同:
>>> %timeit method3b()
96.6 ms ± 3.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
享受吧 第一部分:熊猫和(也许)努比
比较方法1b和方法2:
- method1b生成一个数据帧,这可能是您想要的
- 方法2生成一个Numpy数组,以便得到完全可比的结果, 随后,您应该从中生成一个数据帧
- 调用
获取底层Numpy数组arr=df.values
- 使用Numpy方法对其执行所有必需的操作
- (可选)从最终reslut创建数据帧
一个可供选择的方法是使用NUMA包-A。 Python编译器
我做了这样的测试: 创建Numpy数组(作为初步步骤): 原因是JIT编译的方法能够使用Numpy方法和类型, 但是不是熊猫的 为了进行测试,我使用了与上面几乎相同的方法, 但是使用@njit注释(需要从numba导入njit): 这次:- 执行时间约为method1b时间的45%
- 但是由于在测试循环之前执行了
, 人们怀疑这一结果是否与早期的测试结果具有可比性a=df.values
无论如何,你可以自己试试Numba,也许它对你来说是一个有趣的选择。为什么你需要删除行而不是只使用
df\u new=df[df.array\u condition!=1]
?谢谢你的建议。我同意这样更好。在原来的帖子中添加了这个。这仍然比numpy慢一点,所以我仍然对改进感兴趣。谢谢
def method2():
masking = array_condition != 1
array1_new = array1[masking]
array2_new = array2[masking]
array3_new = array3[masking]
array_condition_new = array_condition[masking]
df_new = pd.DataFrame({ 'array_condition': array_condition[masking],
'array1': array1_new, 'array2': array2_new, 'array3': array3_new})
def method3():
a = df.values
arr = a[a[:,0] != 1]
a = df.values
@njit
def method4():
arr = a[a[:,0] != 1]