【问题标题】:Efficiently reshaping arrays in a pandas dataframe column有效地重塑熊猫数据框列中的数组
【发布时间】:2017-10-11 13:13:39
【问题描述】:

我有一个问题:

让我们考虑一个像这样的 pandas 数据框:

Width  Height  Bitmap

67     56    <1d numpy array with length 67 * 56>
59     71    <1d numpy array with length 59 * 71>
61     73    <1d numpy array with length 61 * 73>
...    ...   ...

现在,我想将 numpy.reshape() 函数应用于位图列中的每一行。结果,它应该如下所示:

Width  Height  Bitmap

67     56    <2d numpy array with shape 67x56 >
59     71    <2d numpy array with shape 59x71 >
61     73    <2d numpy array with shape 61x73>
...    ...   ...

我有一个如下所示的可行解决方案:

for idx, bitmap in df['bitmap'].iteritems():
    df['bitmap'][idx] = np.reshape(bitmap, (df['width'][idx], df['height'][idx]))

我的位图数据框非常大(1,200,000 行),所以我想有效地应用 np.reshape()。有可能吗?

【问题讨论】:

    标签: python performance pandas numpy reshape


    【解决方案1】:

    这行得通吗?

    b2 = []
    Temp = df.apply(lambda x: b2.append(x.Bitmap.reshape(x.Width,x.Height)), axis=1)
    df.Bitmap = b2
    

    【讨论】:

    • 似乎它试图将其应用于每一列:AttributeError: ("'Series' object has no attribute 'bitmap'", 'occured at index width')
    • 现在错误是异常:数据必须是一维的。从追溯来看,并不清楚异常发生在哪个地方。
    • 好的,我再看看。
    • 不是很优雅但是可以再试一次吗?
    【解决方案2】:

    我会保留循环,但是一旦进入循环,我会尝试通过预先计算/存储数组中的宽度和高度值,然后在循环内访问它们来减少计算。访问数组应该会更快。此外,我们将修改 shape 参数,而不是在循环中进行整形。

    因此,实现将是 -

    def arr1d_2D(df):
        r = df.width.values
        c = df.height.values
        n = df.shape[0]
        for i in range(n):
            df.iloc[i,2].shape = (r[i],c[i])
    

    我们可以在这里使用所有 NumPy 来处理 bitmap 列的基础数据,这应该会快得多 -

    def arr1d_2D_allNumPy(df):
        r = df.width.values
        c = df.height.values
        n = df.shape[0]
        b = df['bitmap'].values
        for i in range(n):
            b[i].shape = (r[i],c[i])
    

    示例运行 -

    In [9]: df
    Out[9]: 
       width  height                                bitmap
    0      3       2                    [0, 1, 7, 4, 8, 1]
    1      2       2                          [7, 3, 8, 6]
    2      2       4              [6, 8, 6, 4, 7, 0, 6, 2]
    3      4       3  [8, 6, 5, 2, 2, 2, 4, 3, 3, 3, 1, 8]
    4      4       3  [3, 8, 4, 8, 6, 4, 2, 3, 8, 7, 7, 4]
    
    In [10]: arr1d_2D_allNumPy(df)
    
    In [11]: df
    Out[11]: 
       width  height                                        bitmap
    0      3       2                      [[0, 1], [7, 4], [8, 1]]
    1      2       2                              [[7, 3], [8, 6]]
    2      2       4                  [[6, 8, 6, 4], [7, 0, 6, 2]]
    3      4       3  [[8, 6, 5], [2, 2, 2], [4, 3, 3], [3, 1, 8]]
    4      4       3  [[3, 8, 4], [8, 6, 4], [2, 3, 8], [7, 7, 4]]
    

    运行时测试

    方法-

    def org_app(df):   # Original approach
        for idx, bitmap in df['bitmap'].iteritems():
            df['bitmap'][idx] = np.reshape(bitmap, (df['width'][idx], \
                                                    df['height'][idx]))
    

    时间安排 -

    In [43]: # Setup input dataframe and two copies for testing
        ...: a = np.random.randint(1,5,(1000,2))
        ...: df = pd.DataFrame(a, columns=(('width','height')))
        ...: n = df.shape[0]
        ...: randi = np.random.randint
        ...: df['bitmap'] = [randi(0,9,(df.iloc[i,0]*df.iloc[i,1])) for i in range(n)]
        ...: 
        ...: df_copy1 = df.copy()
        ...: df_copy2 = df.copy()
        ...: df_copy3 = df.copy()
        ...: 
    
    In [44]: %timeit org_app(df_copy1)
    1 loops, best of 3: 26 s per loop
    
    In [45]: %timeit arr1d_2D(df_copy2)
    10 loops, best of 3: 115 ms per loop
    
    In [46]: %timeit arr1d_2D_allNumPy(df_copy3)
    1000 loops, best of 3: 475 µs per loop
    
    In [47]: 26000000/475.0  # Speedup with allNumPy version over original
    Out[47]: 54736.84210526316
    

    疯狂的 50,000x+ 加速,只是展示了访问数据的更好方法,特别是 pandas 数据帧中的数组数据。

    【讨论】:

    • 哇,它确实比我在问题中提出的解决方案快得多。在没有重塑功能的情况下重塑数组是否安全,但手动这样做?
    • @bartekm3 没错。特别是.iloc,我们已经可以访问底层数组数据,所以它不那么混乱,而且同样高效。
    • 谢谢你的帮助:)
    • 让我们看看:数据框有 100 个示例(我从原始数据集中取了一小部分,因为原始方法太慢而无法处理完整数据),但我认为这足以使比较:原始方法:6.05 s ± 24.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 你的方法:10.3 ms ± 10.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 这给了我大约。 2400 倍 提升。感人的。编辑:完整的 numpy 方法:58.2 µs ± 905 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)。大约27000 提升。
    猜你喜欢
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    相关资源
    最近更新 更多