完成此Pandas作业的方法比使用Apply for large数据集更快?

Gre*_*dot 15 python pandas

我有一个由两个不同对象组成的CSV文件的大型数据集:"object_a"和"object_b".这些实体中的每一个都具有数字"tick"值.

Type,       Parent Name, Ticks
object_a,   4556421,     34
object_a,   4556421,     0
object_b,   4556421,     0
object_a,   3217863,     2
object_b,   3217863,     1
......
Run Code Online (Sandbox Code Playgroud)

每个对象共享一个"父名称"值,因此在大多数情况下,每个对象中的一个将共享一个"父名称"值,但情况并非总是如此.

这个数据集有两个目标:

  • 在父名称下提取所有object_a,其中i)有> 1个object_a和; ii)object_a有0个刻度,但另一个object_a有> 0个刻度.即只是零刻度的那个

  • 在父名称下提取所有object_b,其中i)有> = 1 object_a和; ii)object_b有0个刻度,但object_a有> 0个刻度

我的第一种方法是为两个任务分别使用两个函数,以块的形式读取CSV文件(通常大小为1.5GB),并根据父名称将提取的行输出到另一个csv文件...

def objective_one(group_name, group_df):

   group_df = group_df[group_df['Type'] == 'object_a']

   if len(group_df) > 1:

       zero_tick_object_a = group_df[group_df['Ticks'] == 0]

       if len(zero_click_object_a) < len(group_df):

           return zero_click_object_a

       else:

           return pd.DataFrame(columns=group_df.columns)
   else:

       return pd.DataFrame(columns=group_df.columns)



def objective_two(group_name, group_df):

   object_a_in_group_df = group_df[group_df['Type'] == 'object_a']
   object_b_has_no_clicks_in_group_df = group_df[(group_df['Type'] == 'object_b') & (group_df['Ticks'] == 0)]

   if len(object_a_in_group_df) >= 1 and len(object_b_has_no_ticks_in_group_df) >= 1:

       has_ticks_objects = objects_in_group_df[object_a_in_group_df['Ticks'] >= 1]

       if len(has_ticks_object_a) > 0:

           return object_B_has_no_ticks_in_group_df

       else:

           return pd.DataFrame(columns=group_df.columns)
   else:

       return pd.DataFrame(columns=group_df.columns)
Run Code Online (Sandbox Code Playgroud)

以下是main方法中对这些函数的调用:

for chunk in pd.read_csv(file, chunksize=500000):

   #objective one
   chunk_object_a = chunk.groupby(['Parent Name']).apply(lambda g: objective_one(g.name, g))
   ....
   ....
   #objective two
   chunk_object_b = chunk.groupby(['Parent Name']).apply(lambda g: objective_two(g.name, g))
Run Code Online (Sandbox Code Playgroud)

#然后将apply方法输出的数据帧写入csv文件

这种方法的问题在于,虽然它确实得到了我想要的输出,但在1GB及以上范围内的大文件中它非常慢.另一个问题是从CSV中以块的形式读取它可能会有效地将一些组切成两半(即,父名称可以分成一个块而下一个块,使得提取的对象数量不准确)

有没有什么方法可以优化它以使其更快,并且绕过我的块问题?

3kt*_*3kt 3

我对这个问题的看法:

  • 提取父名称下的所有 object_a,其中 i) 有 >1 个 object_a 并且;ii) object_a 有 0 个刻度,但另一个 object_a 有 >0 个刻度。即只是零刻度的那个
  • 提取父名称下的所有 object_b,其中 i) 有 >=1 object_a 并且;ii) object_b 有 0 个刻度,但 object_a 有 >0 个刻度

阅读本文时我的第一印象是,实际的“类型”并不重要,我们只想要object_a每个组都有 >0 刻度的现有元素,并提取所有带有 0 刻度的元素,无论其类型如何。

考虑到这一点,我的方法是首先创建一个新列来计算object_a任何父级的刻度数。如果这个数字>0,则意味着至少object_a存在1个Ticks>0。

In [63]: df.groupby(['Parent Name']).apply(lambda x: x[x['Type'] == 'object_a']['Ticks'].sum())
Out[63]: 
Parent Name
3217863     2
4556421    34
dtype: int64
Run Code Online (Sandbox Code Playgroud)

现在让我们将其合并到原始 DataFrame 中......

In [64]: sumATicks = df.groupby(['Parent Name']).apply(lambda x: x[x['Type'] == 'object_a']['Ticks'].sum())

In [65]: merged = df.merge(pd.DataFrame(sumATicks).rename(columns={0: 'nbATicks'}), left_on='Parent Name', right_index=True)

In [66]: merged
Out[66]: 
       Type  Parent Name  Ticks  nbATicks
0  object_a      4556421     34        34
1  object_a      4556421      0        34
2  object_b      4556421      0        34
3  object_a      3217863      2         2
4  object_b      3217863      1         2
Run Code Online (Sandbox Code Playgroud)

...并根据我上面所述的标准提取所有有趣的行:

In [67]: merged[(merged['nbATicks'] > 0) & (merged['Ticks'] == 0)]
Out[67]: 
       Type  Parent Name  Ticks  nbATicks
1  object_a      4556421      0        34
2  object_b      4556421      0        34
Run Code Online (Sandbox Code Playgroud)

希望我没有忘记任何边缘案例......

关于块问题,为什么不将整个 csv 文件加载到内存中?如果它那么大,您可以尝试在处理之前按 ParentName 排序,并在相关位置拆分块。