Python 数据分析知识点总结

基于个人学习笔记整理,聚焦数据分析实战方向


一、Python 基础特性

Python 是一门解释型弱类型动态语言,非常适合快速开发和数据分析。

  • 解释型:边解释边运行,灵活易改,适合探索性分析
  • 弱类型:变量无需声明类型,隐式转换
  • 动态型:运行时确定数据类型,处理各种格式的数据非常方便

二、数据类型(数据分析核心)

2.1 字符串(str)- 数据清洗的基础

# f-string 格式化(推荐,最简洁)
name = '张三'
age = 25
print(f'{name}今年{age}岁')

# 常用字符串方法(数据清洗必备)
text = "  Hello, World!  "
text.strip()           # 去除两端空格 → 'Hello, World!'
text.lower()           # 转小写 → '  hello, world!  '
text.upper()           # 转大写 → '  HELLO, WORLD!  '

# 分割与拼接(CSV解析常用)
data = "张三,25,北京"
parts = data.split(",")     # ['张三', '25', '北京']
joined = "-".join(parts)    # '张三-25-北京'

# 查找与替换(数据清洗)
log = "Error: 文件未找到"
log.replace("Error", "Warning")  # 'Warning: 文件未找到'
log.find("文件")                  # 返回位置:7
log.count("未")                  # 统计次数:1

# 类型判断(数据验证)
"12345".isdigit()    # True - 判断是否为纯数字
"hello".isalpha()    # True - 判断是否为纯字母

2.2 列表(list)- 数据存储的核心

# 增删改查
data = [10, 20, 30, 40, 50]

# 增
data.append(60)           # 末尾追加 → [10, 20, 30, 40, 50, 60]
data.extend([70, 80])     # 批量追加 → [10, 20, 30, 40, 50, 60, 70, 80]
data.insert(0, 5)         # 指定位置插入 → [5, 10, 20, 30, 40, 50, 60, 70, 80]

# 删
data.remove(30)           # 按值删除(第一个匹配项)
data.pop(0)               # 按索引删除,返回被删除的值
data.pop()                # 默认删除最后一个
del data[0:2]             # 删除切片

# 查
data = [10, 20, 30, 40, 50]
data.index(30)            # 返回索引:2
30 in data                # 成员判断:True
data.count(30)            # 统计次数:1

# 排序
scores = [85, 92, 78, 90, 88]
sorted(scores)                    # 升序,返回新列表
sorted(scores, reverse=True)      # 降序,返回新列表
scores.sort()                     # 原地升序排序
scores.sort(reverse=True)         # 原地降序排序

# 自定义排序(数据分析常用)
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78}
]
sorted(students, key=lambda x: x["score"], reverse=True)  # 按成绩降序

2.3 字典(dict)- 结构化数据的关键

# 创建
person = {"name": "张三", "age": 25, "city": "北京"}

# 查
person.get("name")        # '张三'(推荐,不存在返回None)
person.get("phone", "无") # '无'(设置默认值)
person.keys()             # 获取所有键
person.values()           # 获取所有值
person.items()            # 获取所有键值对

# 增改
person["email"] = "zhangsan@example.com"  # 新增
person["age"] = 26                        # 修改
person.update({"age": 27, "phone": "138xxxx"})  # 批量更新

# 删
person.pop("email")       # 删除指定键值对,返回值
del person["phone"]       # 删除指定键值对

# 遍历(数据分析常用)
for key, value in person.items():
    print(f"{key}: {value}")

2.4 集合(set)- 数据去重与集合运算

# 核心用途:去重
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_data = list(set(data))  # [1, 2, 3, 4]

# 集合运算(数据分析中用于对比两组数据)
set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}

set_a & set_b    # 交集 → {4, 5}
set_a | set_b    # 并集 → {1, 2, 3, 4, 5, 6, 7, 8}
set_a - set_b    # 差集 → {1, 2, 3}
set_a ^ set_b    # 对称差集 → {1, 2, 3, 6, 7, 8}

2.5 元组(tuple)- 不可变数据

# 特点:不可修改,常用于函数返回多个值
coordinates = (116.4, 39.9)  # 北京坐标

# 单元素元组必须加逗号
single = (42,)    # 这是元组
not_tuple = (42)  # 这是整数

# 解包(数据分析常用)
x, y = coordinates

三、类型转换(数据处理必备)

# 基本类型转换
int("42")          # 字符串转整数 → 42
int(3.14)          # 浮点转整数 → 3(截断,不是四舍五入)
float("3.14")      # 字符串转浮点 → 3.14
str(12345)         # 数字转字符串 → '12345'

# 集合类型转换
list("hello")      # 字符串转列表 → ['h', 'e', 'l', 'l', 'o']
tuple([1, 2, 3])   # 列表转元组 → (1, 2, 3)
set([1, 2, 2, 3])  # 列表转集合(去重)→ {1, 2, 3}

# 字典转换(数据分析常用)
keys = ['name', 'age', 'city']
values = ['张三', 25, '北京']
dict(zip(keys, values))  # {'name': '张三', 'age': 25, 'city': '北京'}

# 安全转换(处理脏数据)
def safe_int(value, default=0):
    try:
        return int(value)
    except (ValueError, TypeError):
        return default

safe_int("123")   # 123
safe_int("abc")   # 0
safe_int(None)    # 0

四、推导式(高效数据处理)

4.1 列表推导式

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

# 生成平方数列表
squares = [x**2 for x in range(1, 11)]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 筛选偶数
evens = [x for x in range(1, 21) if x % 2 == 0]
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 数据清洗示例:去除空字符串
raw_data = ["张三", "", "李四", "  ", "王五"]
clean_data = [x.strip() for x in raw_data if x.strip()]
# ['张三', '李四', '王五']

4.2 字典推导式

# 语法:{键:值 for 变量 in 可迭代对象 if 条件}

# 创建平方数字典
square_dict = {x: x**2 for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 数据转换示例:将列表转为字典
names = ['张三', '李四', '王五']
scores = [85, 92, 78]
score_dict = {name: score for name, score in zip(names, scores)}
# {'张三': 85, '李四': 92, '王五': 78}

4.3 集合推导式

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

# 去重并转换为集合
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_set = {x for x in data}
# {1, 2, 3, 4}

五、函数(数据处理逻辑封装)

5.1 基本函数定义

# 位置参数
def calculate_average(scores):
    """计算平均分"""
    return sum(scores) / len(scores)

# 默认参数
def format_number(num, decimal_places=2):
    """格式化数字,保留指定小数位"""
    return round(num, decimal_places)

# 可变参数(处理不定长数据)
def sum_all(*args):
    """计算所有参数的和"""
    return sum(args)

sum_all(1, 2, 3, 4, 5)  # 15

# 关键字参数(灵活配置)
def create_report(title, **kwargs):
    """创建报告,支持自定义属性"""
    report = {"title": title}
    report.update(kwargs)
    return report

create_report("销售报告", author="张三", date="2024-01-01")

5.2 匿名函数(lambda)

# 语法:lambda 参数: 表达式

# 常用于排序和数据转换
square = lambda x: x ** 2
square(5)  # 25

# 排序场景
students = [
    {"name": "张三", "age": 20},
    {"name": "李四", "age": 18},
    {"name": "王五", "age": 22}
]
sorted(students, key=lambda s: s["age"])  # 按年龄升序

5.3 内置函数(数据分析常用)

# 数学计算
abs(-5)              # 绝对值 → 5
round(3.14159, 2)    # 四舍五入 → 3.14
sum([1, 2, 3, 4])    # 求和 → 10
min([5, 3, 8, 1])    # 最小值 → 1
max([5, 3, 8, 1])    # 最大值 → 8
divmod(17, 5)        # 商和余数 → (3, 2)

# 序列操作
len([1, 2, 3])       # 长度 → 3
sorted([3, 1, 2])    # 排序 → [1, 2, 3]
reversed([1, 2, 3])  # 反转迭代器
enumerate(['a', 'b', 'c'])  # 带索引遍历
zip([1, 2], ['a', 'b'])    # 配对

# 高级函数
map(lambda x: x**2, [1, 2, 3])      # 映射 → [1, 4, 9]
filter(lambda x: x > 2, [1, 2, 3, 4])  # 过滤 → [3, 4]

# 类型检查
isinstance(42, int)          # True
isinstance("hello", str)     # True
type(42)                     # <class 'int'>

六、文件操作(数据读写)

6.1 文件读写基础

# 读取文件
with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()           # 读取全部内容
    # 或逐行读取
    for line in f:
        print(line.strip())

# 写入文件
with open("output.txt", "w", encoding="utf-8") as f:
    f.write("第一行\n")
    f.write("第二行\n")

# 追加写入
with open("log.txt", "a", encoding="utf-8") as f:
    f.write("新日志内容\n")

6.2 文件打开模式

模式说明模式说明
r只读(默认)rb二进制只读
w写入(覆盖)wb二进制写入
a追加写入ab二进制追加
r+读写rb+二进制读写

6.3 JSON 数据处理(API 数据常用)

import json

# Python对象 → JSON字符串
data = {"name": "张三", "age": 25, "scores": [85, 92, 78]}
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)  # {"name": "张三", "age": 25, "scores": [85, 92, 78]}

# JSON字符串 → Python对象
parsed = json.loads(json_str)
print(parsed["name"])  # 张三

# 写入JSON文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 读取JSON文件
with open("data.json", "r", encoding="utf-8") as f:
    loaded_data = json.load(f)

6.4 CSV 数据处理

import csv

# 写入CSV
data = [
    ["姓名", "年龄", "城市"],
    ["张三", 25, "北京"],
    ["李四", 30, "上海"],
    ["王五", 28, "广州"]
]

with open("data.csv", "w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# 读取CSV
with open("data.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

七、常用模块(数据分析工具箱)

7.1 os 模块 - 文件系统操作

import os

os.getcwd()                    # 获取当前工作目录
os.listdir(".")                # 列出当前目录文件
os.path.exists("data.csv")    # 判断文件是否存在
os.path.isfile("data.csv")    # 判断是否为文件
os.path.isdir("data")         # 判断是否为目录
os.path.join("data", "file.csv")  # 拼接路径
os.path.split("/data/file.csv")   # 拆分路径 → ('/data', 'file.csv')
os.path.splitext("file.csv")      # 拆分扩展名 → ('file', '.csv')

os.mkdir("new_folder")        # 创建目录
os.makedirs("a/b/c")          # 递归创建目录
os.remove("file.txt")         # 删除文件
os.rename("old.txt", "new.txt")  # 重命名

7.2 json 模块 - 结构化数据

(已在文件操作部分介绍)

7.3 time 模块 - 时间处理

import time

time.time()           # 当前时间戳
time.localtime()      # 本地时间结构体
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())  # 格式化时间
time.sleep(1)         # 暂停1秒

# 常用格式化符号
# %Y: 四位年份  %m: 月份  %d: 日期
# %H: 24小时制  %M: 分钟  %S: 秒

7.4 random 模块 - 随机数据生成

import random

random.random()              # [0, 1) 随机浮点数
random.randint(1, 100)       # [1, 100] 随机整数
random.uniform(1.0, 10.0)    # [1.0, 10.0] 随机浮点数
random.choice([1, 2, 3, 4])  # 随机选择一个元素
random.sample([1, 2, 3, 4, 5], 3)  # 随机选择3个不重复元素
random.shuffle([1, 2, 3, 4])  # 原地打乱列表

7.5 hashlib 模块 - 数据校验

import hashlib

# 计算字符串哈希值
data = "Hello, World!"
md5_hash = hashlib.md5(data.encode()).hexdigest()
print(f"MD5: {md5_hash}")

sha256_hash = hashlib.sha256(data.encode()).hexdigest()
print(f"SHA256: {sha256_hash}")

# 计算文件哈希值(校验文件完整性)
def file_hash(filepath, algorithm="md5"):
    h = hashlib.new(algorithm)
    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            h.update(chunk)
    return h.hexdigest()

八、异常处理(健壮的数据处理)

# 基本结构
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"错误: {e}")
except Exception as e:
    print(f"未知错误: {e}")
else:
    print("没有异常发生")
finally:
    print("无论如何都会执行")

# 实际应用:安全读取数据
def safe_read_json(filepath):
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        print(f"文件不存在: {filepath}")
        return None
    except json.JSONDecodeError:
        print(f"JSON格式错误: {filepath}")
        return None
    except Exception as e:
        print(f"读取失败: {e}")
        return None

# 自定义异常
class DataFormatError(Exception):
    """数据格式异常"""
    pass

def validate_age(age):
    if not isinstance(age, int) or age < 0 or age > 150:
        raise DataFormatError(f"无效的年龄: {age}")
    return True

九、面向对象基础(数据模型封装)

9.1 类与对象

class Student:
    """学生类"""

    def __init__(self, name, age, scores):
        self.name = name
        self.age = age
        self.scores = scores

    def average_score(self):
        """计算平均分"""
        return sum(self.scores) / len(self.scores)

    def __str__(self):
        return f"Student(name={self.name}, age={self.age})"

# 创建对象
student = Student("张三", 20, [85, 92, 78])
print(student)              # Student(name=张三, age=20)
print(student.average_score())  # 85.0

9.2 继承

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        return f"我叫{self.name},今年{self.age}岁"

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)  # 调用父类构造方法
        self.student_id = student_id

    def introduce(self):
        return f"{super().introduce()},学号{self.student_id}"

student = Student("张三", 20, "2024001")
print(student.introduce())  # 我叫张三,今年20岁,学号2024001

9.3 封装(私有属性)

class BankAccount:
    def __init__(self, balance=0):
        self.__balance = balance  # 私有属性

    @property
    def balance(self):
        """获取余额"""
        return self.__balance

    @balance.setter
    def balance(self, amount):
        """设置余额(带验证)"""
        if amount < 0:
            raise ValueError("余额不能为负数")
        self.__balance = amount

account = BankAccount(1000)
print(account.balance)  # 1000
account.balance = 2000  # 正常
# account.balance = -100  # 抛出异常

十、实战技巧汇总

10.1 数据清洗常用模式

# 1. 去除空值和空白
data = ["张三", "", "  ", "李四", None]
clean = [x.strip() for x in data if x and x.strip()]

# 2. 类型转换容错
def safe_float(value, default=0.0):
    try:
        return float(value)
    except (ValueError, TypeError):
        return default

# 3. 数据去重(保持顺序)
def deduplicate(data):
    seen = set()
    return [x for x in data if not (x in seen or seen.add(x))]

# 4. 扁平化嵌套列表
def flatten(lst):
    return [item for sublist in lst for item in sublist]

nested = [[1, 2], [3, 4], [5, 6]]
flatten(nested)  # [1, 2, 3, 4, 5, 6]

10.2 数据统计常用模式

from collections import Counter

# 统计元素出现次数
data = ["苹果", "香蕉", "苹果", "橙子", "香蕉", "苹果"]
counter = Counter(data)
print(counter.most_common(2))  # [('苹果', 3), ('香蕉', 2)]

# 分组统计
students = [
    {"name": "张三", "grade": "A"},
    {"name": "李四", "grade": "B"},
    {"name": "王五", "grade": "A"},
]
groups = {}
for s in students:
    groups.setdefault(s["grade"], []).append(s["name"])
# {'A': ['张三', '王五'], 'B': ['李四']}

10.3 文件批量处理

import os
import glob

# 获取所有CSV文件
csv_files = glob.glob("data/*.csv")

# 批量读取并合并
all_data = []
for filepath in csv_files:
    with open(filepath, "r", encoding="utf-8") as f:
        reader = csv.reader(f)
        header = next(reader)  # 跳过表头
        for row in reader:
            all_data.append(row)

print(f"共读取 {len(all_data)} 条记录")

附录:编码规范速查

项目规范
变量名snake_case(小写+下划线)
类名PascalCase(首字母大写)
常量UPPER_CASE(全大写)
私有属性_protected(单下划线)或 __private(双下划线)
缩进4个空格
行长度不超过80字符
导入顺序内置 → 第三方 → 自定义

十一、正则表达式(数据清洗利器)

正则表达式是大规模数据清洗的核心工具,用于文本模式匹配、提取和替换。

11.1 基础语法速查

import re

# 常用元字符
# .   匹配任意字符(除换行)
# \d  匹配数字 [0-9]
# \w  匹配字母数字下划线 [a-zA-Z0-9_]
# \s  匹配空白字符
# \b  匹配单词边界
# ^   匹配开头
# $   匹配结尾

# 量词
# *   0次或多次
# +   1次或多次
# ?   0次或1次
# {n} 恰好n次
# {n,m} n到m次

11.2 核心函数

import re

text = "联系电话:13812345678,邮箱:test@example.com"

# re.search() - 查找第一个匹配
match = re.search(r"1[3-9]\d{9}", text)
if match:
    print(match.group())  # 13812345678

# re.findall() - 查找所有匹配
phones = re.findall(r"1[3-9]\d{9}", "13812345678 15987654321")
print(phones)  # ['13812345678', '15987654321']

# re.sub() - 替换
cleaned = re.sub(r"\d{3}\*{4}\d{4}", "***", "手机:138****5678")

# re.split() - 分割
parts = re.split(r"[;;,,]+", "Python;Java;C++")
print(parts)  # ['Python', 'Java', 'C++']

# re.compile() - 预编译(大量匹配时性能更好)
pattern = re.compile(r"1[3-9]\d{9}")
result = pattern.findall("13812345678 15987654321")

11.3 分组提取

import re

# 命名分组
text = "2024-01-15"
match = re.search(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})", text)
if match:
    print(match.group("year"))   # 2024
    print(match.group("month"))  # 01

# 非捕获分组
text = "hello123world456"
result = re.findall(r"(?:\d+)", text)  # ['123', '456']

11.4 数据清洗实战模式

import re

# 1. 清理HTML标签
html = "<p>这是<b>一段</b>文本</p>"
text = re.sub(r"<[^>]+>", "", html)  # 这是一段文本

# 2. 提取邮箱
text = "联系:test@example.com 或 admin@company.cn"
emails = re.findall(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text)

# 3. 清理多余空白
text = "  多   个   空格  "
cleaned = re.sub(r"\s+", " ", text).strip()  # 多 个 空格

# 4. 批量替换模式(数据清洗常用)
RE_WELFARE = re.compile(r"(五险一金|带薪年假|节日福利|补充医疗保险)", re.IGNORECASE)
RE_CONTACT = re.compile(r"(联系电话|联系人|Tel|Phone)[::]\s*\S+", re.IGNORECASE)

def clean_text(text):
    text = RE_WELFARE.sub("", text)
    text = RE_CONTACT.sub("", text)
    return text.strip()

十二、pathlib 模块(现代路径处理)

pathlib 是 Python 3.4+ 推荐的路径处理方式,比 os.path 更优雅。

from pathlib import Path

# 创建路径对象
p = Path("data") / "output" / "result.csv"
print(p)  # data/output/result.csv

# 路径属性
p.name       # 'result.csv'
p.stem       # 'result'
p.suffix     # '.csv'
p.parent     # Path('data/output')

# 文件操作
p.exists()          # 是否存在
p.is_file()         # 是否为文件
p.is_dir()          # 是否为目录
p.read_text()       # 读取文本内容
p.write_text("内容")  # 写入文本

# 目录操作
Path("new_dir").mkdir(parents=True, exist_ok=True)  # 递归创建目录

# 遍历目录
for f in Path("data").glob("*.csv"):
    print(f.name)

# 递归遍历
for f in Path("data").rglob("*.csv"):
    print(f)

十三、collections 模块(高级数据结构)

13.1 Counter - 计数器

from collections import Counter

# 统计词频
words = ["苹果", "香蕉", "苹果", "橙子", "香蕉", "苹果"]
counter = Counter(words)

counter.most_common(2)  # [('苹果', 3), ('香蕉', 2)]
counter["苹果"]          # 3
counter["西瓜"]          # 0(不存在返回0)

# 合并计数器
c1 = Counter(a=1, b=2)
c2 = Counter(b=3, c=4)
c1 + c2  # Counter({'b': 5, 'c': 4, 'a': 1})

13.2 defaultdict - 默认字典

from collections import defaultdict

# 自动初始化不存在的键
groups = defaultdict(list)
students = [
    ("张三", "A班"), ("李四", "B班"), ("王五", "A班")
]
for name, cls in students:
    groups[cls].append(name)
# {'A班': ['张三', '王五'], 'B班': ['李四']}

# 统计词频(不用setdefault)
word_count = defaultdict(int)
for word in ["a", "b", "a", "c", "b", "a"]:
    word_count[word] += 1
# {'a': 3, 'b': 2, 'c': 1}

13.3 OrderedDict - 有序字典

from collections import OrderedDict

# Python 3.7+ 普通dict已保证插入顺序
# OrderedDict 额外支持 move_to_end() 和相等比较(考虑顺序)

od = OrderedDict()
od["a"] = 1
od["b"] = 2
od["c"] = 3
od.move_to_end("a")  # 移到末尾
# OrderedDict([('b', 2), ('c', 3), ('a', 1)])

十四、类型提示(代码质量保障)

类型提示让代码更易读、更易维护,配合 IDE 可以提前发现错误。

14.1 基本类型提示

def greet(name: str) -> str:
    return f"Hello, {name}"

def calculate_average(scores: list[float]) -> float:
    return sum(scores) / len(scores)

# 变量类型提示
age: int = 25
name: str = "张三"
scores: list[int] = [85, 92, 78]

14.2 复杂类型提示

from typing import Dict, List, Set, Tuple, Optional, Union

# 字典类型
def get_user(name: str) -> Dict[str, Union[str, int]]:
    return {"name": name, "age": 25}

# 可选类型(可能为None)
def find_user(user_id: int) -> Optional[Dict[str, str]]:
    if user_id > 0:
        return {"name": "张三"}
    return None

# 元组类型
def get_coordinates() -> Tuple[float, float]:
    return (116.4, 39.9)

# 集合类型
def get_unique_tags(text: str) -> Set[str]:
    return set(text.split(";"))

14.3 typing 模块高级用法

from typing import Callable, Any

# 回调函数类型
def process_data(data: list, callback: Callable[[Any], Any]) -> list:
    return [callback(item) for item in data]

# Python 3.10+ 新语法
def greet(name: str | None = None) -> str:
    return f"Hello, {name or 'World'}"

十五、日志系统(生产级数据处理)

15.1 基本使用

import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)

# 使用
logging.info("开始处理数据...")
logging.warning("发现缺失值")
logging.error("文件读取失败")

15.2 日志级别

级别数值用途
DEBUG10调试信息
INFO20一般信息
WARNING30警告
ERROR40错误
CRITICAL50严重错误

15.3 实战配置

import logging

# 创建 logger
logger = logging.getLogger("data_pipeline")
logger.setLevel(logging.DEBUG)

# 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 文件处理器
file_handler = logging.FileHandler("pipeline.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG)

# 格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 使用
logger.info("Pipeline 启动")
logger.debug("处理第 1000 条记录")

十六、数据处理进阶技巧

16.1 生成器(节省内存)

# 处理大文件时,逐行读取避免内存溢出
def read_large_file(filepath):
    with open(filepath, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

# 使用
for line in read_large_file("huge_data.csv"):
    process(line)

# 生成器表达式(比列表推导式更省内存)
sum(x**2 for x in range(1000000))  # 不会创建完整列表

16.2 上下文管理器

# 自定义上下文管理器
class Timer:
    def __enter__(self):
        import time
        self.start = time.time()
        return self

    def __exit__(self, *args):
        import time
        self.elapsed = time.time() - self.start
        print(f"耗时: {self.elapsed:.2f}秒")

# 使用
with Timer() as t:
    # 执行耗时操作
    pass
print(f"总耗时: {t.elapsed}秒")

16.3 dataclass(数据类)

from dataclasses import dataclass
from typing import List

@dataclass
class JobRecord:
    """岗位记录"""
    name: str
    company: str
    salary: str
    skills: List[str]
    score: float = 0.0

    def is_high_salary(self) -> bool:
        return "面议" not in self.salary and "k" in self.salary.lower()

# 使用
job = JobRecord("算法工程师", "某科技公司", "30-50k", ["Python", "PyTorch"])
print(job)  # JobRecord(name='算法工程师', company='某科技公司', ...)

附录 B:数据分析常用正则模式速查

场景正则表达式说明
手机号1[3-9]\d{9}11位手机号
邮箱[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}标准邮箱格式
身份证\d{17}[\dXx]18位身份证
IP地址\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}IPv4地址
中文字符[一-龥]匹配单个中文
HTML标签<[^>]+>匹配HTML标签
空白字符\s+匹配一个或多个空白
数字\d+匹配一个或多个数字

这份笔记涵盖了数据分析方向最常用的 Python 基础知识。建议配合 NumPy、Pandas、Matplotlib 等库一起学习,构建完整的数据分析技能栈。