Python3 高级数据处理:推导式大法

Python3 高级数据处理:推导式大法

    • 一、推导式:Python的数据魔法 ✨
    • 二、列表推导式:一行创建列表的艺术
      • 基本语法
      • 生活中的类比
      • 基础示例
      • 带条件过滤的列表推导式
      • 多重循环的列表推导式
      • 条件表达式(三元运算符)在列表推导式中的应用
      • 实际应用场景
    • 三、字典推导式:键值对的快速构建
      • 基本语法
      • 生活中的类比
      • 基础示例
      • 从现有字典创建新字典
      • 合并和转换数据
      • 处理嵌套结构
    • 四、集合推导式:唯一元素的快速集合
      • 基本语法
      • 基础示例
      • 实际应用
    • 五、生成器表达式:惰性求值的艺术 ‍♂️
      • 生活中的类比
      • 列表推导式 vs 生成器表达式
      • 实际应用
    • 六、推导式嵌套:多维数据处理
      • 嵌套列表推导式
      • 嵌套字典推导式
    • 七、推导式性能优化
      • 测量性能差异
      • 推导式的局限性
    • 九、推导式最佳实践
      • 何时使用推导式
      • 可读性优先
    • 十、推导式面试题
    • 练习
    • 1. 文本处理:提取长度大于3的单词并转换为大写
    • 2. 数据分析:找出平均分超过90分的学生
    • 3. 矩阵操作:旋转矩阵90度
    • 推导式小结

一、推导式:Python的数据魔法 ✨

推导式(Comprehensions)是Python中最强大的语法特性之一,它让我们能用简洁优雅的方式来创建和转换数据集合。它们不仅仅是语法糖,更是提高代码可读性和性能的利器。

“简单是可靠的先决条件” — Edsger W. Dijkstra

二、列表推导式:一行创建列表的艺术

基本语法

[表达式 for 变量 in 可迭代对象 if 条件]

生活中的类比

列表推导式就像一个快速食品加工机:你放入原材料(可迭代对象),设置筛选条件(if语句),然后每个通过筛选的食材都经过同样的加工(表达式),最终得到一盘精心处理过的成品。

基础示例

# 传统方式:计算1到10的平方
squares = []
for x in range(1, 11):
    squares.append(x ** 2)
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 列表推导式方式
squares = [x ** 2 for x in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

带条件过滤的列表推导式

# 获取1到20中所有偶数的平方
even_squares = [x ** 2 for x in range(1, 21) if x % 2 == 0]
print(even_squares)  # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

多重循环的列表推导式

# 生成坐标对
coordinates = [(x, y) for x in range(1, 4) for y in range(1, 3)]
print(coordinates)  # [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)]

# 等价的传统写法
coordinates = []
for x in range(1, 4):
    for y in range(1, 3):
        coordinates.append((x, y))

条件表达式(三元运算符)在列表推导式中的应用

# 判断数字是奇数还是偶数
numbers = [1, 2, 3, 4, 5]
result = ["偶数" if x % 2 == 0 else "奇数" for x in numbers]
print(result)  # ['奇数', '偶数', '奇数', '偶数', '奇数']

实际应用场景

# 数据清洗:过滤并转换数据
data = ["10", "error", "20", "N/A", "30", ""]
clean_data = [int(x) for x in data if x.isdigit()]
print(clean_data)  # [10, 20, 30]

# 文件处理:提取特定行
with open('example.txt', 'r') as file:
    non_empty_lines = [line.strip() for line in file if line.strip()]

# 扁平化嵌套列表
nested_list = [[1, 2, 3], [4, 5], [6, 7, 8]]
flat_list = [item for sublist in nested_list for item in sublist]
print(flat_list)  # [1, 2, 3, 4, 5, 6, 7, 8]

三、字典推导式:键值对的快速构建

基本语法

{键表达式: 值表达式 for 变量 in 可迭代对象 if 条件}

生活中的类比

字典推导式就像一个自动标签机:你给它一堆物品(可迭代对象),它按照你的规则给每个物品贴上相应的标签(键),并可能修改物品本身(值),最终生成一个有序的标签-物品对应表。

基础示例

# 传统方式:创建数字及其平方的映射
square_dict = {}
for x in range(1, 6):
    square_dict[x] = x ** 2
print(square_dict)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 字典推导式方式
square_dict = {x: x ** 2 for x in range(1, 6)}
print(square_dict)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

从现有字典创建新字典

# 价格美元转人民币 (假设汇率 1:7)
prices = {'apple': 0.5, 'banana': 0.3, 'orange': 0.6}
prices_rmb = {fruit: price * 7 for fruit, price in prices.items()}
print(prices_rmb)  # {'apple': 3.5, 'banana': 2.1, 'orange': 4.2}

# 筛选价格低于0.5美元的水果
cheap_fruits = {fruit: price for fruit, price in prices.items() if price < 0.5}
print(cheap_fruits)  # {'banana': 0.3}

合并和转换数据

# 合并两个列表成字典
fruits = ['apple', 'banana', 'orange']
counts = [5, 3, 2]
fruit_inventory = {fruit: count for fruit, count in zip(fruits, counts)}
print(fruit_inventory)  # {'apple': 5, 'banana': 3, 'orange': 2}

# 交换键值对
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped)  # {1: 'a', 2: 'b', 3: 'c'}

处理嵌套结构

# 统计每个单词的字母出现频率
word = "hello"
letter_count = {letter: word.count(letter) for letter in set(word)}
print(letter_count)  # {'h': 1, 'e': 1, 'l': 2, 'o': 1}

# 处理嵌套字典
student_scores = {
    'Alice': {'math': 90, 'science': 85, 'english': 95},
    'Bob': {'math': 80, 'science': 90, 'english': 85}
}

# 获取每个学生的平均分
average_scores = {
    name: sum(scores.values()) / len(scores) 
    for name, scores in student_scores.items()
}
print(average_scores)  # {'Alice': 90.0, 'Bob': 85.0}

四、集合推导式:唯一元素的快速集合

基本语法

{表达式 for 变量 in 可迭代对象 if 条件}

基础示例

# 获取列表中所有偶数,并去重
numbers = [1, 2, 2, 3, 4, 4, 5, 6, 6]
even_set = {x for x in numbers if x % 2 == 0}
print(even_set)  # {2, 4, 6}

实际应用

# 提取文本中的所有独特字符
text = "hello world"
unique_chars = {char for char in text if char.isalpha()}
print(unique_chars)  # {'e', 'd', 'h', 'l', 'o', 'r', 'w'}

# 获取两个列表的交集
list1 = [1, 2, 3, 4, 5]
list2 = [3, 4, 5, 6, 7]
intersection = {x for x in list1 if x in list2}
print(intersection)  # {3, 4, 5}

五、生成器表达式:惰性求值的艺术 ‍♂️

生成器表达式的语法与列表推导式相似,但使用圆括号而非方括号:

(表达式 for 变量 in 可迭代对象 if 条件)

生活中的类比

生成器表达式就像按需配送服务:不会一次性生产所有商品,而是在客户需要时才生产下一个商品,从而节省存储空间。

列表推导式 vs 生成器表达式

import sys

# 列表推导式:立即计算所有值
squares_list = [x ** 2 for x in range(1000)]
print(f"列表占用内存: {sys.getsizeof(squares_list)} 字节")

# 生成器表达式:按需计算值
squares_gen = (x ** 2 for x in range(1000))
print(f"生成器占用内存: {sys.getsizeof(squares_gen)} 字节")

实际应用

# 处理大文件时节省内存
with open('large_file.txt', 'r') as file:
    line_lengths = (len(line.strip()) for line in file)
    total_length = sum(line_lengths)  # 只遍历文件一次

# 链式操作
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sum_of_even_squares = sum(x ** 2 for x in numbers if x % 2 == 0)
print(sum_of_even_squares)  # 220

六、推导式嵌套:多维数据处理

嵌套列表推导式

# 创建一个3x4的矩阵
matrix = [[i * 4 + j + 1 for j in range(4)] for i in range(3)]
print(matrix)
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

# 矩阵转置
transposed = [[row[i] for row in matrix] for i in range(4)]
print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

嵌套字典推导式

# 创建嵌套字典
students = ['Alice', 'Bob', 'Charlie']
subjects = ['Math', 'Science', 'English']

# 为每个学生创建一个字典,包含各科的初始分数
grades = {
    student: {subject: 0 for subject in subjects}
    for student in students
}
print(grades)
# {'Alice': {'Math': 0, 'Science': 0, 'English': 0},
#  'Bob': {'Math': 0, 'Science': 0, 'English': 0},
#  'Charlie': {'Math': 0, 'Science': 0, 'English': 0}}

七、推导式性能优化

测量性能差异

import time

# 测量列表创建性能
def test_performance():
    # 使用普通循环
    start = time.time()
    result = []
    for i in range(1000000):
        if i % 2 == 0:
            result.append(i ** 2)
    loop_time = time.time() - start
    
    # 使用列表推导式
    start = time.time()
    result = [i ** 2 for i in range(1000000) if i % 2 == 0]
    comp_time = time.time() - start
    
    print(f"循环时间: {loop_time:.4f}秒")
    print(f"推导式时间: {comp_time:.4f}秒")
    print(f"速度提升: {loop_time/comp_time:.2f}倍")

test_performance()

推导式的局限性

# 不适合复杂逻辑
def complex_operation(x):
    # 假设这是一个复杂的、多步骤的操作
    result = x
    for _ in range(3):
        if result % 2 == 0:
            result = result // 2
        else:
            result = 3 * result + 1
    return result

# 这种情况下,传统循环更清晰
results = []
for x in range(1, 11):
    results.append(complex_operation(x))

# 虽然可以写成推导式,但可读性降低
results = [complex_operation(x) for x in range(1, 11)]

九、推导式最佳实践

何时使用推导式

适合用推导式的场景

  • 简单的转换和过滤操作
  • 创建新集合而不修改原集合
  • 代码可以清晰地表达在一行内

不适合用推导式的场景

  • 复杂的逻辑操作
  • 有多个副作用的操作
  • 需要详细注释解释的转换
  • 嵌套层次过多(超过2层)

可读性优先

# 过长难读
result = [x for x in range(100) if x % 2 == 0 if x % 3 == 0 if x % 5 == 0]

# 更清晰的版本
result = [
    x 
    for x in range(100) 
    if x % 2 == 0 
    if x % 3 == 0 
    if x % 5 == 0
]

# 或者使用一个条件
result = [x for x in range(100) if x % 30 == 0]

十、推导式面试题

问题1:解释以下代码的输出

squares = [x**2 for x in range(5)]
doubles = [2*x for x in squares if x % 2 == 0]
print(doubles)

问题2:实现一个函数,使用字典推导式将一个字符串中的字符计数

def char_count(text):
    """计算字符串中每个字符的出现次数"""
    return {char: text.count(char) for char in set(text)}

问题3:重构以下代码为推导式

result = []
for i in range(10):
    if i % 2 == 0:
        for j in range(i):
            if j % 2 == 1:
                result.append((i, j))

解答3

result = [(i, j) for i in range(10) if i % 2 == 0 for j in range(i) if j % 2 == 1]

练习

  1. 文本处理:编写一个推导式,从一个句子中提取所有长度大于3的单词,并转换为大写

  2. 数据分析:给定一个学生成绩字典,使用推导式找出所有平均分超过90分的学生

  3. 矩阵操作:使用嵌套推导式实现一个函数,可以将任意大小的矩阵旋转90度

1. 文本处理:提取长度大于3的单词并转换为大写

def extract_and_capitalize(sentence):
    """从句子中提取所有长度大于3的单词,并转换为大写"""
    return [word.upper() for word in sentence.split() if len(word) > 3]

# 测试
sentence = "Python is a powerful programming language that is widely used"
result = extract_and_capitalize(sentence)
print(result)  # 输出: ['PYTHON', 'POWERFUL', 'PROGRAMMING', 'LANGUAGE', 'THAT', 'WIDELY', 'USED']

这个推导式由三部分组成:

  • word.upper() - 转换操作
  • for word in sentence.split() - 迭代部分,将句子分割为单词
  • if len(word) > 3 - 过滤条件,只保留长度大于3的单词

2. 数据分析:找出平均分超过90分的学生

def find_top_students(student_grades):
    """找出所有平均分超过90分的学生"""
    return {
        name: grades 
        for name, grades in student_grades.items() 
        if sum(grades) / len(grades) > 90
    }

# 测试
student_grades = {
    "Alice": [95, 92, 98, 88],
    "Bob": [85, 82, 88, 75],
    "Charlie": [92, 95, 96, 98],
    "Diana": [88, 91, 87, 89]
}

top_students = find_top_students(student_grades)
print(top_students)  # 输出: {'Alice': [95, 92, 98, 88], 'Charlie': [92, 95, 96, 98]}

这个字典推导式:

  • 保留原始的学生名字和成绩列表
  • 遍历学生成绩字典的所有项
  • 仅保留平均分超过90的学生

3. 矩阵操作:旋转矩阵90度

def rotate_matrix_90(matrix):
    """将矩阵顺时针旋转90度"""
    # 使用嵌套推导式,按列从下到上获取元素
    return [[matrix[i][j] for i in range(len(matrix)-1, -1, -1)] for j in range(len(matrix[0]))]

# 测试
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

rotated = rotate_matrix_90(matrix)
for row in rotated:
    print(row)

# 输出:
# [7, 4, 1]
# [8, 5, 2]
# [9, 6, 3]

这个嵌套推导式的工作原理:

  1. 外层推导式 for j in range(len(matrix[0])) 遍历原矩阵的列
  2. 内层推导式 for i in range(len(matrix)-1, -1, -1) 从下到上遍历原矩阵的行
  3. 结果是将原矩阵的每一列(从下到上)变成新矩阵的每一行

这实现了矩阵的顺时针旋转90度。如果要逆时针旋转,只需修改内层循环的方向或调整列的遍历顺序。

推导式是Python中非常强大的特性,可以让代码更简洁、更具可读性,同时保持高效率。上面的例子展示了列表推导式、字典推导式和嵌套推导式的不同用法。

推导式小结

推导式是Python中独特而强大的特性,掌握它们能够:

  • 提高代码简洁性:用一行代码替代多行循环
  • 提升性能:推导式通常比等效循环更快
  • 增强可读性:一旦习惯,推导式比嵌套循环更容易理解
  • 减少错误:更少的代码意味着更少的错误机会

但记住,推导式不是万能的,最重要的原则是保持代码可读性。如果一个推导式变得过于复杂,考虑使用传统循环或函数拆分它。

“简单始终优于复杂” —— Python之禅

你可能感兴趣的:(Python3,python,开发语言)