Python 加快大型阵列上的操作&;数据集(速度慢,质量好,进一步改进?)

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(

我有一个由数百万行和大约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(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数组,以便得到完全可比的结果, 随后,您应该从中生成一个数据帧
因此,我将您的方法2更改为:

然后比较执行时间(使用%timeit)

结果是我的method2(扩展版)执行时间延长了约5% 方法1b(自己检查)

所以我的观点是,只要涉及到一个单一的操作, 和熊猫呆在一起可能更好

但是如果您想对源数据帧执行一些操作 按顺序和/或您对Numpy数组的结果感到满意, 值得注意的是:

  • 调用
    arr=df.values
    获取底层Numpy数组
  • 使用Numpy方法对其执行所有必需的操作
  • (可选)从最终reslut创建数据帧
我尝试了method1b的Numpy版本:

但是执行时间大约延长了40%

原因可能是Numpy数组具有 相同的类型,所以数组_条件列被强制为float,然后 整个Numpy数组创建完毕,这需要一些时间

第2部分:Numpy和Numba

一个可供选择的方法是使用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]