在每组数据框架内创建点列表

2024-04-16 13:53:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个数据集,其结构如下所示example_df

example_df = pd.DataFrame({'measurement_id': np.concatenate([[0] * 300, [1] * 300]),
                           'min': np.concatenate([np.repeat(range(0, 30), 10), 
                                                  np.repeat(range(0, 30), 10)]),
                           'grp': list(np.repeat(['A', 'B'], 5)) * 60,
                           'grp2': list(np.random.choice([0, 1, 2], 10)) * 60,
                           'obj': np.array(list(range(0, 10)) * 60),
                           'x': np.random.normal(0.0, 10.0, 600),
                           'y': np.random.normal(50.0, 40.0, 600)})

我还有一个函数,它将一组点作为输入并执行一些计算。我想准备我的数据并在分组数据框中创建一个点列表。你知道吗

我目前的解决方案如下:

def df_to_points(df):
    points = []
    for index, row in df.iterrows():
        points.append(tuple(row))
    return(points)

res = example_df \
    .groupby(['measurement_id', 'min', 'grp']) \
    .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])

res.head(5)
measurement_id  min  grp
0               0    A      [[(7.435996920897324, 63.64844826366264), (-9....
                1    B      [[(-10.213911323779579, 108.64263032884301), (...
                2    A      [[(6.004534743892181, 38.11898691750269), (12....
                3    B      [[(-11.486905682289555, 68.26172126981378), (-...
                4    A      [[(7.5612638943199295, 28.756743327333556), (-...

其中res系列的每一行如下所示:

[[(7.435996920897324, 63.64844826366264),
  (-9.722976872232584, 11.831678494223155),
  (10.809492206072777, 82.9238481225157),
  (-7.918248246978473, 58.46902598333271)],
 [(6.270634566510545, 59.10653240815831),
  (-5.765185730532471, 22.232739287056663),
  (-13.129531349093371, 85.02932179274353)],
 [(0.6686875099768917, 60.634711491838786),
  (-7.373072676442981, 30.897262347426693),
  (-11.489744246260528, 6.834296232736001)]] 

问题是,我原来的数据帧有几百万行,感觉这个解决方案可以从一些优化中受益。你知道吗

示例的当前运行时为:

%timeit res = example_df \
    .groupby(['measurement_id', 'min', 'grp']) \
    .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])
289 ms ± 1.36 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此,我的问题是:

  1. numpy多维数组替换元组列表会提高性能吗?你知道吗
  2. 为了提高速度,是否有任何应避免的重大瓶颈?你知道吗

@Edit:一个例子,在grp定义的组中有不同数量的对象

example_df2 =  pd.DataFrame({'measurement_id': np.concatenate([[0] * 300, [1] * 300]),
                           'min': np.concatenate([np.repeat(range(0, 30), 10), 
                                                  np.repeat(range(0, 30), 10)]),
                           'grp': list(np.repeat(['A', 'B', 'C'], [4, 4, 2])) * 60,
                           'grp2': list(np.random.choice([0, 1, 2], 10)) * 60,
                           'obj': np.array(list(range(0, 10)) * 60),
                           'x': np.random.normal(0.0, 10.0, 600),
                           'y': np.random.normal(50.0, 40.0, 600)})

Tags: 数据iddfexamplenprangerandommin
3条回答

在使用array = np.array(df)迭代之前,可以将整个数据帧转换为numpy数组。它一定会提高性能。 您还可以使用多线程模块来并行处理并获得性能。 您也可以使用熊猫.apply(),而不是使用.iterrows()

可以使用.pivot_table()aggfunc=简单list

example_df['combined'] = example_df[['x', 'y']].values.tolist()
example_df = example_df.pivot_table(index=['measurement_id', 'min', 'grp'], columns=['grp2'], values=['combined'], aggfunc=list)
example_df['res'] = example_df.values.tolist()
example_df = example_df.drop(columns=['combined'])

印刷品:

                                                                      res
grp2                                                                     
measurement_id min grp                                                   
0              0   A    [[[0.9303000896627107, 42.806752849742715], [-...
               1   B    [[[-18.605643711859955, 117.83261611194004], [...
               2   A    [[[-7.304055455430749, 18.06452177236371], [-1...
...

使用timeit进行基准测试:

example_df = pd.DataFrame({'measurement_id': np.concatenate([[0] * 300, [1] * 300]),
                           'min': np.concatenate([np.repeat(range(0, 30), 10),
                                                  np.repeat(range(0, 30), 10)]),
                           'grp': list(np.repeat(['A', 'B'], 10)) * 30,
                           'grp2': list(np.random.choice([0, 1, 2], 10)) * 60,
                           'obj': np.array(list(range(0, 10)) * 60),
                           'x': np.random.normal(0.0, 10.0, 600),
                           'y': np.random.normal(50.0, 40.0, 600)})

def get_df():
    return example_df.copy()

def solution_1():
    def df_to_points(df):
        points = []
        for index, row in df.iterrows():
            points.append(tuple(row))
        return(points)

    example_df = get_df()
    res = example_df \
        .groupby(['measurement_id', 'min', 'grp']) \
        .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])
    return res

def solution_2():
    example_df = get_df()
    example_df['combined'] = example_df[['x', 'y']].values.tolist()
    example_df = example_df.pivot_table(index=['measurement_id', 'min', 'grp'], columns=['grp2'], values=['combined'], aggfunc=list)
    example_df['res'] = example_df.values.tolist()
    example_df = example_df.drop(columns=['combined'])
    return example_df

t1 = timeit(lambda: solution_1(), number=100)
t2 = timeit(lambda: solution_2(), number=100)

print(t1)
print(t2)

印刷品:

21.74300919502275
3.124330924008973

编辑:通过更新问题,您可以执行以下操作:

example_df['combined'] = example_df[['x', 'y']].values.tolist()
example_df = example_df.pivot_table(index=['measurement_id', 'min', 'grp'], columns=['grp2'], values=['combined'], aggfunc=list)
example_df.apply(lambda x: list(x[x.notna()]), axis=1)

基准:

from timeit import timeit

example_df = pd.DataFrame({'measurement_id': np.concatenate([[0] * 300, [1] * 300]),
                           'min': np.concatenate([np.repeat(range(0, 30), 10),
                                                  np.repeat(range(0, 30), 10)]),
                           'grp': list(np.repeat(['A', 'B'], 5)) * 60,
                           'grp2': list(np.random.choice([0, 1, 2], 10)) * 60,
                           'obj': np.array(list(range(0, 10)) * 60),
                           'x': np.random.normal(0.0, 10.0, 600),
                           'y': np.random.normal(50.0, 40.0, 600)})

example_df =  pd.DataFrame({'measurement_id': np.concatenate([[0] * 300, [1] * 300]),
                           'min': np.concatenate([np.repeat(range(0, 30), 10),
                                                  np.repeat(range(0, 30), 10)]),
                           'grp': list(np.repeat(['A', 'B', 'C'], [4, 4, 2])) * 60,
                           'grp2': list(np.random.choice([0, 1, 2], 10)) * 60,
                           'obj': np.array(list(range(0, 10)) * 60),
                           'x': np.random.normal(0.0, 10.0, 600),
                           'y': np.random.normal(50.0, 40.0, 600)})

def get_df():
    return example_df.copy()

def solution_1():
    def df_to_points(df):
        points = []
        for index, row in df.iterrows():
            points.append(tuple(row))
        return(points)

    example_df = get_df()
    res = example_df \
        .groupby(['measurement_id', 'min', 'grp']) \
        .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])
    return res

def solution_2():
    example_df = get_df()
    example_df['combined'] = example_df[['x', 'y']].values.tolist()
    example_df = example_df.pivot_table(index=['measurement_id', 'min', 'grp'], columns=['grp2'], values=['combined'], aggfunc=list)
    return example_df.apply(lambda x: list(x[x.notna()]), axis=1)

t1 = timeit(lambda: solution_1(), number=100)
t2 = timeit(lambda: solution_2(), number=100)

print(t1)
print(t2)

印刷品:

45.391786905995104
13.506823723029811

一个轻微的优化是:

def df_to_points(df): 
    return [tuple(x) for x in df.values]

然后你得到

In [59]: %timeit res = example_df \ 
    ...:     .groupby(['measurement_id', 'min', 'grp']) \ 
    ...:     .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])                                        
241 ms ± 14.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

与之相反(使用df_to_points的原始定义)

In [58]: %timeit res = example_df \ 
    ...:     .groupby(['measurement_id', 'min', 'grp']) \ 
    ...:     .apply(lambda x: [df_to_points(g[['x', 'y']]) for _, g in x.groupby('grp2')])                                        
284 ms ± 10.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

相关问题 更多 >