Python 比使用Apply for large data set更快地完成此任务?

Python 比使用Apply for large data set更快地完成此任务?,python,pandas,Python,Pandas,我有一个由两个不同对象组成的大型CSV文件数据集:object\u a和object\u b。这些实体中的每一个都有一个数值勾号值 Type, Parent Name, Ticks object_a, 4556421, 34 object_a, 4556421, 0 object_b, 4556421, 0 object_a, 3217863, 2 object_b, 3217863, 1 ...... 每个对象共享一个父

我有一个由两个不同对象组成的大型CSV文件数据集:
object\u a
object\u b
。这些实体中的每一个都有一个数值
勾号

Type,       Parent Name, Ticks
object_a,   4556421,     34
object_a,   4556421,     0
object_b,   4556421,     0
object_a,   3217863,     2
object_b,   3217863,     1
......
每个对象共享一个
父名称
值,因此在大多数情况下,每个对象中的一个将共享一个
父名称
值,但情况并非总是如此

我对这个数据集有两个目标:

  • 提取父名称下的所有对象,其中i)有>1个对象和;ii)对象a有0个刻度,但另一个对象a有>0个刻度。i、 e.只有一个零刻度
  • 提取父名称下的所有对象,其中i)有>=1个对象,并且;ii)对象_b具有0个刻度,但对象_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)
#然后将apply方法输出的数据帧写入csv文件

这种方法的问题是,虽然它确实能获得我想要的输出,但在1GB及以上的大文件中,它的速度非常慢。另一个问题是,从CSV中分块读取可能会有效地将某些组一分为二(即,父名称可能会分为一个块和下一个块,从而导致提取的对象数量不准确)


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

我对这个问题的看法:

  • 提取父名称下的所有对象,其中i)有>1个对象和;ii)对象_a有0个刻度,但另一个对象_a 有>0个刻度。i、 e.只有一个零刻度
  • 提取父名称下的所有对象,其中i)有>=1个对象,并且;ii)对象_b具有0个刻度,但对象_a具有>0个刻度 滴答声
读这篇文章时,我的第一印象是,实际的“类型”并不重要,我们只需要一个现有的
object\u a
,每个组有>0个刻度,并提取所有有0个刻度的元素,不管它们的类型如何

考虑到这一点,我的方法是首先创建一个新列来计算任何父项的
object\u a
刻度数。如果该数字大于0,则表示至少存在1个勾号大于0的
object\u a

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
现在让我们将其合并到原始数据帧中

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
…并根据上述标准提取所有感兴趣的行:

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
希望我没有忘记任何附带案件


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

我的想法如下:

我认为第一个目标更容易,因为我们只依赖于带有object_a的行。我们可以使用transform将条件转换为布尔列表:

df_1 = df.loc[df['Type']=='object_a']
object_a = df_1.loc[(df_1.groupby('Parent_Name')['Ticks'].transform(min)==0)&
                    (df_1.groupby('Parent_Name')['Ticks'].transform(max)>0)&
                    (a['Ticks']==0)
                   ]
Out[1]: 
       Type  Parent_Name  Ticks
1  object_a      4556421      0
对于第二个目标,我创建了一个符合object_a要求的父_名称列表。在下一步中,isin仅用于选择相应的行

a_condition = df.loc[df['Type']=='object_a'].groupby('Parent_Name').sum()
a_condition = a_condition[a_condition>0].index

object_b = df.loc[(df['Type']=='object_b')&
                  (df['Ticks']==0)&
                  (df['Parent_Name'].isin(a_condition))
                 ]
Out[2]: 
       Type  Parent_Name  Ticks
2  object_b      4556421      0
将数据聚合到
元组中

In [33]: df1 = df.groupby(['Parent Name',
                           'Type']).agg(lambda x: tuple(x)).unstack(1)

In [34]: df1
Out[34]: 
                      Ticks         
Type               object_a object_b
       Parent Name                  
3217863                (2,)     (1,)
4556421             (34, 0)     (0,)
为您的案例构建布尔掩码#1

为您的案例构建布尔掩码#2

获取案例1的结果

获取案例2的结果


就效率而言,接下来要做什么,而不是如何创建csv。在函数声明之前添加
@numba.jit
。它将把这个函数编译成字节码。
In [35]: df
Out[32]: 
       Type         Parent Name   Ticks
0  object_a             4556421      34
1  object_a             4556421       0
2  object_b             4556421       0
3  object_a             3217863       2
4  object_b             3217863       1
In [33]: df1 = df.groupby(['Parent Name',
                           'Type']).agg(lambda x: tuple(x)).unstack(1)

In [34]: df1
Out[34]: 
                      Ticks         
Type               object_a object_b
       Parent Name                  
3217863                (2,)     (1,)
4556421             (34, 0)     (0,)
In [35]: mask1 = df1.apply(lambda x: (len(x[0])>1) & ((x[0]).count(0)==1), 
                           axis=1)

In [36]: mask1
Out[36]: 
       Parent Name
3217863    False
4556421     True
dtype: bool
In [37]: mask2 = df1.apply(lambda x: ((len(x[0])>=1) & 
                                      (len(set(x[0]).difference([0]))>0) &
                                      (len(x[1])==1) & 
                                      (x[1][0]==0)),
                           axis=1)

In [38]: mask2
Out[38]: 
       Parent Name
3217863    False
4556421     True
dtype: bool
In [39]: df1.loc[mask1, [('Ticks', 'object_a')]]
Out[39]: 
                      Ticks
Type               object_a
       Parent Name         
4556421             (34, 0)
In [30]: df1.loc[mask2, [('Ticks', 'object_b')]]
Out[30]: 
                      Ticks
Type               object_b
       Parent Name         
4556421                (0,)