我有一个由一百万条记录组成的大数据集(在下面的代码片段中表示为只有 5 行的big_df(,我想在调用调用classify function
的 apply 函数时使用多处理。
问题陈述 数据集由 1M 组成,每行包括列id
和name
,最新的列name
包括标点符号和停用词,这些标点符号和停用词使用 regex 删除。然后,我们可以假设数据帧中显示的名称已被清理。
对于每条记录,都需要逐字逐句地确定该词是在开头还是/和结尾使用,它可以是两者兼而有之。例如,如果我们有单词ABC SECONDARY SCHOOL BOARDING
:
-
ABC
只是在同名ABC SCHOOL BOARDING
的开头, 它没有classification end
.然后,它在classification start
处计为 1 . -
SECONDARY
是第二个词,它不是 在开头和结尾都没有找到。然后,它被分类 作为none
. SCHOOL
是第三个词,它出现在MARIE INSTITUTE SCHOOL
、RALPH ELEMENTARY SCHOOL
和BOARDING SCHOOL
的末尾。 然后,它在classification end
处算作 3 .BOARDING
是第四个单词,在ABC SCHOOL BOARDING
的末尾找到,在classification end
处算作 1。另外,BOARDING
在开头找到BOARDING SCHOOL
,则计为: 1 在classification start
.
此过程一直持续到到达所有行。然后,执行聚合以获取单词在开头和/或结尾的次数,如果没有,则该单词被归类为none
。
以下是使用apply调用函数classify
的工作版本:
import pandas as pd
Data=[[0,'ABC SECONDARY SCHOOL BOARDING',['ABC','SECONDARY','SCHOOL','BOARDING']],
[1,'UNIVERSITY BOARDING INSTITUTE',['UNIVERSITY','BOARDING','INSTITUTE']],
[2,'MARIE INSTITUTE SCHOOL',['MARIE', 'INSTITUTE','SCHOOL']],
[3,'RALPH ELEMENTARY SCHOOL',['RALPH','ELEMENTARY','SCHOOL']],
[4,'BOARDING SCHOOL',['BOARDING','SCHOOL']]]
df_big=pd.DataFrame(Data, columns=['id','name', 'name_list'])
df_class = pd.DataFrame(columns=['word','classification'])
df_class2 = pd.DataFrame(columns=['word','classification'])
def classify(row,start,end):
d=[]
for word in row.name_list:
flag=False
if word in start.values:
d.append([word,'start'])
flag=True
if word in end.values:
d.append([word,'end'])
flag=True
if (not flag):
d.append([word,'none'])
return d
df_start=pd.DataFrame(columns=['name'])
df_end=pd.DataFrame(columns=['name'])
df_start= df_big.name.str.split().str.get(0).drop_duplicates(keep="last")
df_end= df_big.name.str.split().str.get(-1).drop_duplicates(keep="last")
lst=[]
lst = df.apply(classify, args=[df_start, df_end],axis=1)
flat_list = [item for sublist in lst for item in sublist]
lst=[]
for e in flat_list:
lst.append(e)
print("--------")
print("Dataframe classified:")
df2=pd.DataFrame(lst, columns=['word', 'classification'])
print(df2)
print("Summary")
df2.index.names = ['id']
df2.reset_index(inplace=True, drop=True)
count_series=df2.groupby(['word','classification']).size()
df_count = count_series.to_frame(name = 'counter').reset_index()
print(df_count)
这将提供如下结果:
Dataframe classified:
word classification
0 ABC start
1 SECONDARY none <------SPECIAL CASE, NOT CLASSIFIED
2 SCHOOL end
3 BOARDING start
4 BOARDING end
5 UNIVERSITY start
6 BOARDING start
7 BOARDING end
8 INSTITUTE end
9 MARIE start
10 INSTITUTE end
11 SCHOOL end
12 RALPH start
13 ELEMENTARY none <------SPECIAL CASE, NOT CLASSIFIED
14 SCHOOL end
15 BOARDING start
16 BOARDING end
17 SCHOOL end
Summary
word classification counter
0 ABC start 1
1 BOARDING end 3
2 BOARDING start 3
3 ELEMENTARY none 1 <---NONE
4 INSTITUTE end 2
5 MARIE start 1
6 RALPH start 1
7 SCHOOL end 4
8 SECONDARY none 1 <---NONE
9 UNIVERSITY start 1
基本上,每个单词都分为start
、end
或none
。
上述版本正在工作并产生显示的结果。但是,对于由百万条记录组成的数据集,这会延迟大约 10 个小时。我需要以更快的方式做同样的事情。在我的搜索中,我想出了multiprocessing
,通过根据内核数量拆分大数据集并并行运行数据集的每个部分来帮助加快该过程。
然后,我添加了以下修改:
def process(df,df_start,df_end):
return df.apply(classify, args=[df_start, df_end],axis=1)
p = mp.Pool(processes=8)
split_dfs = np.array_split(big_df,8)
pool_results = p.map(process, split_dfs)
p.close()
p.join()
# merging parts processed by different processes
parts = pd.concat(pool_results, axis=0)
# merging newly calculated parts to big_df
big_df = pd.concat([big_df, parts], axis=1)
# checking if the dfs were merged correctly
pdt.assert_series_equal(parts['id'], big_df['id'])
但是,此修改仍在运行,没有结果。我想知道如何以更快的方式使用多个参数使用 apply((。
感谢您的帮助:)
Python(以及固有的 Pandas(经常难以处理更大的数据集(特别是如果函数没有矢量化 - 请参阅有关如何改进它的体面文章此处(没有一些特殊的设置。
将其放在次要位置并尝试解决手头的问题(在值列表中查找匹配的单词(:
您可以为end
单词创建一个数据框,为start
单词创建一个数据框(假设单词既可以是start
又可以是end
(。
然后,您需要进行两次左合并。这些应该很快。
out_start=pd_big.merge(pd_start,left_on='word',right_on='word',how='left')
out_both=out_start.merge(pd_end,left_on='word',right_on='word',how='left')
您可能需要进行一些重新命名,但这应该要快得多。
你可以在这里找到更多关于熊猫合并的信息:(https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html(