在numpy数组上循环时性能较差

0g0grzrc  于 2023-06-29  发布在  其他
关注(0)|答案(2)|浏览(62)

使用函数get_height,我计算每次扫描两个点云之间的高度差(在我的示例中是y,z坐标)。
我的算法有效,但平均需要1.93秒。如何提高性能?
编辑:我附上了一个完整的工作示例

import numpy as np
import matplotlib.pyplot as plt

def generate_random_dataset(N,x_max):
    # Create the 'x' column
    unique_x = np.linspace(0, x_max, x_max*10+1)
    x = np.random.choice(unique_x, N) # Generate the array with repeated values

    # Create the 'y' column
    y = np.random.uniform(-5, 5, N)

    # Create the 'z' column
    z = - y**2 + 5 + np.random.normal(0, 1, N)

    # Create the 'A' array
    A = np.column_stack((x, y, z))

    return A

def get_height(A0,A1):
    # get unique x values that are in both scans
    ux0 = np.unique(A0[:,0])
    ux1 = np.unique(A1[:,0])
    ux = np.intersect1d(ux0,ux1)

    # get height at each unique x value
    h = []
    for x in ux:
        # get slice of lower scan
        mask0 = (A0[:,0] == x)
        z0 = A0[mask0,2]
        
        # get slice of upper scan
        mask1 = (A1[:,0] == x)
        z1 = A1[mask1,2]

        # get height difference
        height = np.max(z1) - np.max(z0)

        # append results to list
        h.append(height)

    # convert list to array
    h = np.array(h)

    return ux, h

# run script
A0 = generate_random_dataset(N=300000,x_max=100)
A1 = generate_random_dataset(N=310000,x_max=120)
A1[:,2] = A1[:,2] - 0.001*(A1[:,0]-50)**2 + 5 # make A1 higher and different than A0

# apply function
%timeit ux,h = get_height(A0,A1)
ux0 = np.unique(A0[:,0])
ux1 = np.unique(A1[:,0])
ux = np.intersect1d(ux0,ux1)

# plot
fig = plt.figure(figsize=(4.24*1.5,3*1.5))
ax = plt.subplot(111)
ax.scatter(ux,h)
ax.set_xlabel('x [mm]')
ax.set_ylabel('h [mm]')
plt.show()

我在前面的一个问题中试过使用np.lexsort方法,但是这种方法对两个数组不起作用。
我想用不同的方法来解决这个问题(不对唯一的x值进行循环),但我找不到解决方案。

uemypmqf

uemypmqf1#

可能有一个numpy解决方案,但与此同时,使用pandas比在每次迭代中查找的python循环快得多,甚至包括将数组转换为 Dataframe 的开销。

import pandas as pd

def get_height_pd(A0, A1):
    df0 = pd.DataFrame(A0)
    df1 = pd.DataFrame(A1)
    m0 = df0.groupby(0)[2].max()
    m1 = df1.groupby(0)[2].max()
    return (m1 - m0).dropna()  # dropna gets rid of the non-intersecting ones

或者,使用series,可能会快一点。

def get_height_s(A0, A1):
    s0 = pd.Series(A0[:, 2])
    s1 = pd.Series(A1[:, 2])
    m0 = s0.groupby(A0[:, 0]).max()
    m1 = s1.groupby(A1[:, 0]).max()
    return (m1 - m0).dropna()
avkwfej4

avkwfej42#

下面是使用this function to get the min and max的丑陋的numpy解决方案。转置其中一个数组,使其位于另一个数组的下方且方向相反(y' = offset - y,其中offset是一个合适的小数字),将两个数组连接在一起,然后找到每个x的最小值和最大值。每行中的最小值将是A1的offset - maximum,每行中的最大值将是A0的最大值。然后反转换位以获得高度差。

def agg_minmax(a):  # from https://stackoverflow.com/a/58908648/567595
    sidx = np.lexsort(a[:,::-1].T)
    b = a[sidx]
    m = np.r_[True,b[:-1,0]!=b[1:,0],True]
    return np.c_[b[m[:-1],:2], b[m[1:],1]]

def get_height(A0, A1):
    min0 = A0[:, 2].min()
    offset = min0 + A1[:, 2].min() - 1
    b0 = A0[:, [0, 2]]
    b1 = np.array([A1[:, 0], offset - A1[:, 2]]).T
    c = np.concatenate((b0, b1))
    agg = agg_minmax(c)
    f = agg[(agg[:, 1] < min0) & (agg[:, 2] >= min0)]   # filter out the not-applicable rows
    return f[:, 0], offset - f[:, 1] - f[:, 2]

它比pandas解决方案慢,但也许可以调整。

相关问题