使用ThreadPoolExecutor多线程处理pandas的DataFrame数据并用tqdm显示处理进度
在pandas数据较大(千万行)时,使用groupby函数并对聚合的数据进行一系列操作需要耗费非常多的时间,但是查看cpu性能时发现cpu占用率非常低。于是萌生使用多线程加速的想法,至少让cpu满载工作。
- 原始代码:
for user, user_hist in reviews_df.groupby(by=['user_id']):
...
这里用的操作比较简单,也有需要进行的操作很复杂不能用lambda操作的情况。
- 使用ThreadPoolExecutor多线程加速处理:
from concurrent.futures import ThreadPoolExecutor
from tqdm import tqdm
start = time.time()
executor = ThreadPoolExecutor(96)
group_list = []
def process_user_hist(x):
user, user_reviews_df = x
user_reviews_df = user_reviews_df.sort_values(by=['t_dat'])
hist = user_reviews_df[['article_id', 'product_type_no']].values
group_list.append(user, hist)
user_iter = reviews_df.groupby(by=['customer_id'])
resutl = list(executor.map(process_user_hist, tqdm(user_iter)))
这里使用ThreadPoolExecutor而非ProcessPoolExecutor,因为不同的线程需要访问同一个内存资源group_list。python存在全局解释器锁(GIL)理论上能防止不同线程同时写一个内存对象,也就是说python里面对象是线程安全的,这里我也跑了两次,验证了跑出来结果长度是正确的。(至少这里的list的append操作是安全的)
- 另外pandas还可以通过swifter来加速apply的处理速度。例如:
import swifter
df.swifter.apply(...)
不过swifter不能对groupby对象使用,这里对我没什么帮助。
- 另外注:多线程方式下比原始方法快很多,cpu占用也是满的。但后续在进行其他的逻辑操作时发现直接用
df.groupby(['column_name']).apply(method_name)竟比多线程方式要快,不知道pandas的apply是否有多线程的优化。