你还在Pandas中使用for循环吗?太慢了!

科技   2024-08-10 10:38   江苏  

来源:Deephub Imba

这篇文章将讲解pandas dataframe循环的各种方法,为了验证循环的有效性,我们将生成百万级别的数据,这也是我们在日常处理中经常遇到的数量级。

实验数据集

我们将生成一个包含600万行和4列的DataFrame。每一列将被分配一个0到50之间的随机整数。

 import numpy as np
 import pandas as pd
 df = pd.DataFrame(np.random.randint(0, 50, size=(6000000, 4)), columns=('a','b','c','d'))
 df.shape
 # (6000000, 5)
 df.head()

Iterrows

我们通过基于以下标准引入一个新的列' e '来扩展数据框架' df ':

如果' a '等于0,那么' e '取' d '的值。如果' a '在0(不包括)到25(包括)的范围内,' e '计算为' b '减去' c '。如果以上条件都不成立,则计算“e”为“b”+“c”。

首先我们使用pandas提供的' iterrows() '函数遍历DataFrame ' df '。' iterrows() '函数遍历DataFrame的行,在迭代期间返回(index, row)对。

 import time
 start = time.time()
 # Iterating through DataFrame using iterrows
 for idx, row in df.iterrows():
    if row.a == 0:
        df.at[idx,'e'] = row.d
     
    elif (row.a <= 25) & (row.a > 0):
        df.at[idx,'e'] = (row.b)-(row.c)
    else:
        df.at[idx,'e'] = row.b + row.c
 end = time.time()
 print(end - start)
 # time taken: 335.212792634964

iterrows()函数需要335秒(约5.5分钟)来实现对600万行的操作。

Itertuples

另一种遍历pandas DataFrame的方法是使用' itertuples ',它以命名元组的形式遍历DataFrame行。

下面代码说明了如何使用' itertuples '访问元素。生成的行对象将索引作为第一个字段,然后是数据框的列。

 for row in df[:1].itertuples():
    print(row) ## accessing the complete row - index following by columns
    print(row.Index) ## accessing the index of the row
    print(row.a) ## accessing the value of column 'a'

使用下面的代码,使用itertuples()遍历DataFrame df。

 start = time.time()
 # Iterating through namedtuples
 for row in df.itertuples():
    if row.a == 0:
        df.at[row.Index,'e'] = row.d
     
    elif (row.a <= 25) & (row.a > 0):
        df.at[row.Index,'e'] = (row.b)-(row.c)
    else:
        df.at[row.Index,'e'] = row.b + row.c
         
 end = time.time()
 print(end - start)
 ## Time taken: 41 seconds

在DataFrame上执行所需的操作,itertuples()函数耗时约54秒,比iterrows()函数快6倍。

字典

迭代DataFrame行的另一种方法是将DataFrame转换为字典,这是一种轻量级的内置数据类型。我们遍历该字典以执行所需的操作,然后将更新后的字典转换回DataFrame。转换可以使用' to_dict() '函数来实现。

 start = time.time()
# converting the DataFrame to a dictionary
df_dict = df.to_dict('records')  
# Iterating through the dictionary
for row in df_dict[:]:
   if row['a'] == 0:
       row['e'] = row['d']
    
   elif row['a'] <= 25 & row['a'] > 0:
       row['e'] = row['b']-row['c']
   else:
       row['e'] = row['b'] + row['c']
# converting back to DataFrame
df4 = pd.DataFrame(df_dict)
 end = time.time()
 print(end - start)
 ## Time taken: 31 seconds

字典方法大约需要31秒,大约比' itertuples() '函数快11倍。

数组列表

我们还可以将DataFrame转换为一个数组,遍历该数组以对每行(存储在列表中)执行操作,然后将该列表转换回DataFrame。

 start = time.time()
 # create an empty dictionary
 list2 = []
 # intialize column having 0s.
 df['e'] = 0
 # iterate through a NumPy array
 for row in df.values:
    if row[0] == 0:
        row[4] = row[3]
     
    elif row[0] <= 25 & row[0] > 0:
        row[4] = row[1]-row[2]
 
    else:
        row[4] = row[1] + row[2]
 ## append values to a list
    list2.append(row)
 ## convert the list to a dataframe  
 df2 = pd.DataFrame(list2, columns=['a', 'b', 'c', 'd','e'])
 end = time.time()
 print(end - start)
 #Time Taken: 21 seconds

花费的时间约为21秒(比iterrows快16倍),这与遍历字典所花费的时间非常接近。

字典和数组是内置的轻量级数据结构,因此迭代DataFrame所需的时间最少。

总结

在文探索了使用循环遍历DataFrame的四种不同方法。

' iterrows '函数在遍历DataFrame时显示出最高的时间消耗。与“iterrows”函数相比,使用“itertuples”函数可以使DataFrame迭代的速度提高6倍。在字典和数组上迭代被证明是最有效的方法,使用循环提供最快的迭代时间和最佳的数据操作。

当然,在处理大型数据集时,最佳实践是矢量化。向量化上述代码将执行时间减少到0.29秒(比遍历数组快72倍)。但是使用矢量化时会增加开发的成本,所以在一些时候为了我们开发方便,可以选择一个比较快速for循环来替代矢量化。当然,如果你对矢量化非常的了解,那还是推荐继续使用。

往期文章

推荐我常用的几个爬虫插件&工具,值得收藏~
推荐3款自动爬虫神器,再也不用手撸代码了
新式爬虫利器,网页解锁能力非常强大!
使用八爪鱼爬虫+Kimi AI分析小米SU7舆情数据,终于知道它为什么火了
推荐我常用的爬虫工具,三种爬虫方式,搞定反爬和动态页面
6个强大且流行的Python爬虫库,强烈推荐!

加入知识星球【我们谈论数据科学】

提供100节专属Pandas数据分析视频教程

600+小伙伴一起学习!





Python大数据分析
分享Python编程、数据分析、爬虫、人工智能等
 最新文章