我有一个由两个不同对象组成的大型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
......
每个对象共享一个"父名称"因此,在大多数情况下,每个对象中的一个将共享一个"父名称"价值,但情况并非总是如此。
我对此数据集有两个目标:
在父名称下提取所有object_a,其中i)有> 1 object_a' s; 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)
以下是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))
#然后将apply方法输出的数据帧写入csv文件
这种方法的问题在于,虽然它确实得到了我想要的输出,但在1GB及以上范围内的大文件中它非常慢。另一个问题是,从CSV中读取块可以有效地将一些组切成两半(即,父名称可以拆分为一个块,而下一个,可以提取不准确的对象数量)
有没有办法优化它以使其更快,还可以解决我的大块问题?
答案 0 :(得分:3)
我对这个问题的看法:
- 在父名称下提取所有object_a,其中i)有&gt; 1 object_a's和; ii)object_a有0个刻度但是另一个object_a 有> 0个刻度。即只是零滴答的
- 在父名称下提取所有object_b,其中i)有&gt; = 1 object_a和; ii)object_b具有0个刻度但object_a具有> 0 蜱
我在阅读本文时的第一印象是,实际的“类型”并不重要,我们只想为每个组提供一个带有&gt; 0 Ticks的现有object_a
,并提取所有0个刻度的元素,不管他们的类型如何。
考虑到这一点,我的方法是首先创建一个新列来计算任何父级的object_a
个滴答数。如果此数字> 0,则表示Ticks&gt; 0至少存在1 object_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
现在让我们将其合并到原始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
...并根据我上面提到的标准提取所有有趣的行:
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文件加载到内存中?如果它 大,你可以在处理之前尝试按ParentName排序,并在相关位置拆分块。
答案 1 :(得分:1)
我的想法是:
我认为第一个目标更容易,因为我们只依赖于具有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要求的Parent_Names列表。在下一步中,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
答案 2 :(得分:1)
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
将数据汇总到tuple
s
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
构建布尔掩码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
为您的案例#2
构建布尔掩码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
获取案例#1的结果
In [39]: df1.loc[mask1, [('Ticks', 'object_a')]]
Out[39]:
Ticks
Type object_a
Parent Name
4556421 (34, 0)
获取案例#2的结果
In [30]: df1.loc[mask2, [('Ticks', 'object_b')]]
Out[30]:
Ticks
Type object_b
Parent Name
4556421 (0,)