Python高级技术之:`Python`的`Pandas`和`NumPy`:如何利用向量化操作避免`for`循环。

各位观众,大家好!我是今天的主讲人,很高兴能和大家一起聊聊Python里Pandas和NumPy这对好基友,以及如何利用它们的向量化操作,优雅地告别那些磨人的for循环。

今天的主题是:Python高级技术之:PythonPandasNumPy:如何利用向量化操作避免for循环。 记住,我们的目标是:能不用for循环,就坚决不用!

第一部分:for循环的痛点

在深入向量化操作之前,我们先来回顾一下for循环。for循环就像一个勤勤恳恳的老黄牛,一条数据一条数据地处理。虽然可靠,但效率实在不敢恭维。

举个例子,假设我们有一个包含100万个数字的列表,现在想把每个数字都乘以2。

import time

data = list(range(1000000))

# 使用for循环
start_time = time.time()
result_for = []
for x in data:
    result_for.append(x * 2)
end_time = time.time()
print(f"For循环耗时: {end_time - start_time:.4f} 秒")

这段代码很简单,但运行起来你会发现,它需要一段时间才能完成。尤其是在处理大数据集时,这种等待会让你怀疑人生。而且代码也不够简洁,可读性也稍差。

第二部分:NumPy的向量化操作:速度与激情的碰撞

NumPy是Python中用于科学计算的核心库。它的核心是ndarray(N-dimensional array)对象,也就是多维数组。NumPy的强大之处在于,它可以对整个数组进行操作,而无需显式的for循环。这就是所谓的向量化操作

让我们用NumPy来重写上面的例子:

import numpy as np
import time

data = np.arange(1000000)

# 使用NumPy向量化操作
start_time = time.time()
result_numpy = data * 2
end_time = time.time()
print(f"NumPy向量化操作耗时: {end_time - start_time:.4f} 秒")

运行这段代码,你会发现速度提升了不止一个档次!这就是向量化操作的魅力所在。

为什么NumPy这么快?

  • 底层优化: NumPy的底层是用C语言实现的,C语言的执行效率远高于Python。
  • SIMD(Single Instruction, Multiple Data): NumPy可以利用CPU的SIMD指令集,同时对多个数据进行相同的操作。

一些常见的NumPy向量化操作:

操作 描述 例子
加法 数组对应元素相加 a + b
减法 数组对应元素相减 a - b
乘法 数组对应元素相乘 a * b
除法 数组对应元素相除 a / b
指数运算 数组每个元素的指数运算 np.exp(a)
对数运算 数组每个元素的对数运算 np.log(a)
平方根运算 数组每个元素的平方根运算 np.sqrt(a)
比较运算 数组对应元素进行比较,返回布尔数组 a > b, a == b
逻辑运算 对布尔数组进行逻辑运算 np.logical_and(a, b), np.logical_or(a, b)
聚合运算 对整个数组进行聚合计算 np.sum(a), np.mean(a), np.max(a)

广播(Broadcasting):

NumPy的广播机制允许对形状不同的数组进行运算。它会自动扩展较小的数组,使其与较大的数组形状匹配。

a = np.array([1, 2, 3])
b = 2
print(a + b)  # 输出:[3 4 5]

在这个例子中,b被广播成了[2, 2, 2],然后与a进行相加。

第三部分:Pandas的向量化操作:数据分析的利器

Pandas是基于NumPy构建的,提供了更高级的数据结构和数据分析工具。其中最核心的数据结构是Series(一维带标签数组)和DataFrame(二维带标签数组,可以看作是表格)。

Pandas也支持向量化操作,这使得对SeriesDataFrame进行数据处理变得非常高效。

Series的向量化操作:

import pandas as pd

s = pd.Series([1, 2, 3, 4, 5])

# 对Series进行向量化操作
s_multiplied = s * 2
print(s_multiplied)

这段代码将Series中的每个元素都乘以2。

DataFrame的向量化操作:

import pandas as pd

data = {'col1': [1, 2, 3], 'col2': [4, 5, 6]}
df = pd.DataFrame(data)

# 对DataFrame进行向量化操作
df_multiplied = df * 2
print(df_multiplied)

这段代码将DataFrame中的每个元素都乘以2。

Pandas中的一些常用向量化操作:

  • 算术运算: +, -, *, /, **
  • 比较运算: ==, !=, >, <, >=, <=
  • 字符串操作: .str属性提供了许多字符串处理方法,例如.str.lower(), .str.upper(), .str.contains()等。
  • 日期时间操作: .dt属性提供了许多日期时间处理方法,例如.dt.year, .dt.month, .dt.day等。
  • .apply()方法: 虽然.apply()方法本身不是向量化操作,但它可以将一个函数应用到SeriesDataFrame的每个元素上。在某些情况下,可以使用.apply()结合NumPy的向量化操作来实现更复杂的功能。
  • .map() 方法: 对Series的每个值应用一个函数或字典映射。

案例分析:避免for循环的几种常见场景

  1. 数据清洗:

    假设我们有一个包含用户信息的DataFrame,其中有一列是用户的电话号码。由于各种原因,这些电话号码的格式可能不统一。我们需要将所有电话号码的格式统一为XXX-XXX-XXXX

    import pandas as pd
    
    data = {'phone_number': ['1234567890', '123-456-7890', '(123) 456-7890']}
    df = pd.DataFrame(data)
    
    # 使用向量化操作清洗电话号码
    df['phone_number'] = df['phone_number'].str.replace(r'[^d]+', '', regex=True)  # 去除非数字字符
    df['phone_number'] = df['phone_number'].str.pad(10, side='left', fillchar='0')  # 补齐10位
    df['phone_number'] = df['phone_number'].str[:3] + '-' + df['phone_number'].str[3:6] + '-' + df['phone_number'].str[6:]  # 添加分隔符
    
    print(df)

    这段代码使用Pandas的字符串操作,避免了使用for循环。

  2. 数据转换:

    假设我们有一个包含销售数据的DataFrame,其中有一列是销售额。我们需要将销售额转换为人民币(假设汇率为6.5)。

    import pandas as pd
    
    data = {'sales': [100, 200, 300]}
    df = pd.DataFrame(data)
    
    # 使用向量化操作转换货币
    exchange_rate = 6.5
    df['sales_rmb'] = df['sales'] * exchange_rate
    
    print(df)

    这段代码直接对DataFrame的整列数据进行乘法运算,非常简洁高效。

  3. 条件判断:

    假设我们有一个包含学生成绩的DataFrame,我们需要根据成绩给学生评级(A, B, C, D)。

    import pandas as pd
    import numpy as np
    
    data = {'score': [90, 80, 70, 60]}
    df = pd.DataFrame(data)
    
    # 使用NumPy的select函数进行条件判断
    conditions = [
        df['score'] >= 90,
        (df['score'] >= 80) & (df['score'] < 90),
        (df['score'] >= 70) & (df['score'] < 80),
        df['score'] < 70
    ]
    choices = ['A', 'B', 'C', 'D']
    df['grade'] = np.select(conditions, choices)
    
    print(df)

    np.select()函数可以根据多个条件,从多个备选项中选择相应的值。避免了使用多个if-else语句或for循环。

  4. 创建新列基于现有列:

    假设我们有一个DataFrame, 需要根据现有列的数值创建一个新的列。例如,根据销售额是否大于1000来创建一个’High Sales’列。

     import pandas as pd
     import numpy as np
    
     data = {'sales': [500, 1200, 800, 1500]}
     df = pd.DataFrame(data)
    
     # 使用布尔索引和np.where创建新列
     df['High Sales'] = np.where(df['sales'] > 1000, 'Yes', 'No')
    
     print(df)

    np.where()函数可以根据条件选择不同的值,这避免了使用循环来逐行检查和赋值。

第四部分:什么时候应该避免使用for循环?

  • 数据量大: 当处理大数据集时,for循环的效率会非常低下。
  • 操作简单: 当需要对数据进行简单的算术运算、比较运算或字符串操作时,向量化操作通常更简洁高效。
  • PandasNumPy提供了现成的函数:PandasNumPy提供了可以直接使用的函数时,应该优先使用这些函数,而不是自己编写for循环。

第五部分:一些注意事项

  • 内存占用: 向量化操作可能会占用更多的内存,因为需要创建新的数组来存储结果。
  • 数据类型: 确保数据类型一致,才能进行向量化操作。例如,不能将字符串和数字直接相加。
  • 复杂逻辑: 对于一些非常复杂的逻辑,可能无法完全避免使用for循环。但即使在这种情况下,也可以尝试将部分操作向量化,以提高效率。

总结

向量化操作是PandasNumPy的核心特性之一,它可以极大地提高数据处理的效率。在编写Python代码时,我们应该尽可能地利用向量化操作,避免使用for循环。这不仅可以提高代码的运行速度,还可以使代码更简洁易懂。希望通过今天的讲解,大家能更加熟练地运用向量化操作,让你的数据分析工作事半功倍!

记住,告别for循环,拥抱向量化!让你的代码飞起来!

今天的讲座就到这里,谢谢大家!如果还有什么问题,欢迎提问。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注