跳转到内容

【限时特惠】小白玩转Python数据分析_哔哩哔哩_bilibili

  • PyCharm 软件较大,安装需等待一段时间,完成后点击完成关闭安装窗口。
  • 接下来打开 PyCharm。
  • 非 Windows 系统可跳过此视频。
  • 从官方渠道下载 Python 解释器和 PyCharm:搜索 Python 官网,找到带官方标签的网页点进去。
  • 访问官网:在浏览器地址栏直接输入 www.python.org 进入官网。

  • 自动识别系统:官网会自动检测当前操作系统版本(如 Windows),确保下载的安装包与系统匹配。

  • 开始下载:

    1. 在导航栏点击 Downloads
    2. 页面会自动推荐适合当前系统的版本,点击页面上的大黄色按钮即可开始下载安装程序。
  • 版本兼容性:下载版本号不必完全一致,只要是 3 开头的版本即可正常使用。
  • 添加环境变量(至关重要):
    • 在安装界面务必勾选 Add Python to PATH
    • 作用:将 Python 路径添加到系统环境变量中,使操作系统能够识别并定位 Python 的安装位置。
  • 完成安装:勾选后点击 Install Now 开始安装,等待进度条走完即可。
  • 路径长度限制:安装完成后,若出现 Disable path length limit 的提示,建议点击该选项以解除 Windows 对路径长度的限制。
  • 点击 close 关闭安装窗口。

  • 验证安装成功:

    1. 在菜单栏搜索 CNB 进入命令提示符。
    2. 输入 Python(全大写)并回车。
    3. 若显示 Python 后跟安装版本及一串杂乱文字,说明电脑已有 Python。
  • 接下来安装 Python:搜索 Python 官网,确认网址 www.jetbrings.com-python,然后找 downloads

  • PyCharm 提供两个版本:
    • Professional (专业版):收费,功能更全面。
    • Community (社区版):免费,对于纯 Python 开发已完全足够。
  • 在官网下载页面向下滚动,找到 Community Edition 进行下载。
  • 运行安装程序后,按照指引点击“下一步”选择安装路径。
  • 安装选项设置(建议根据需求勾选):
    • 桌面图标:方便快速启动。
    • 关联文件:将所有 Python 文件(.py)默认设置为通过 PyCharm 打开。
  • 首次打开 PyCharm,弹出语言和地区选择窗口,默认匹配操作系统(如中文)。
  • 非中文系统无妨,后续可设置成中文。
  • 点击下一个,同意用户协议(Terms)继续。
  • 首次启动时询问是否导入配置,无需则直接跳过。
  • Windows上PyCharm初始化完成,可创建项目并编写代码。
  • 打开PyCharm,若先前设置了语言和地区,可能已为中文界面。
  • 若界面默认为英文,可通过设置将其更改为中文:

    1. 在欢迎页面点击左下角的设置图标(齿轮图标)。
    2. 选择 Preferences(Windows 系统下为 Settings)。
    3. 在左侧菜单中找到并点击 Plugins(插件)。
    4. 若未直接显示,可在左上角的搜索框中输入 plugins 进行查找。
    5. 在插件页面的搜索框内输入 chinese,即可找到中文语言包并进行安装。
  • 若在 Marketplace 中无法搜索到中文语言包,说明插件可能已被默认安装。

  • 检查已安装插件:

    1. 点击插件窗口顶部的 Installed 标签页。
    2. 若插件显示为灰色(禁用状态),勾选该插件。
    3. 点击右下角的 ApplyOK 按钮以激活设置。
  • 语言切换生效:

    • 激活后,重新进入设置菜单(Windows 为 Settings,macOS 为 Preferences)。
    • 在搜索框中输入 language 即可进行后续的语言环境管理。

PyCharm 界面变更为中文与新建项目

Section titled “PyCharm 界面变更为中文与新建项目”
  • 重启后,往上滑动找到 Language and Region 栏,选择 中文,点击 OKRestart

  • 页面语言变为中文。

  • 创建项目集中管理代码文件:

    1. 欢迎页面点击 新建项目
    2. 第一行表示项目文件夹位置,可换成好找的路径。
  • 在创建项目时,必须先在目标位置(如桌面)手动创建一个空文件夹,再将其选为项目路径。
    • 若直接选择桌面作为项目根目录,PyCharm 会将整个桌面视为项目文件夹,导致文件混乱。
  • 项目文件夹应仅用于存放与该项目直接相关的代码文件、素材等,严禁混入个人无关文件(如身份证照片、论文等)。
  • 解释器类型:默认选择项目虚拟环境(venv)。
    • PyCharm 会自动检测电脑上安装的 Python 版本。
    • 若安装了多个版本,可通过下拉菜单手动选择所需的 Python 版本。
  • 项目创建:配置完成后点击“创建”,PyCharm 会自动生成项目结构,包含 venv 等系统文件夹,用于隔离项目依赖。
  • venv 的作用
    • PyCharm 自动创建的 venv 文件夹代表该项目的独立 Python 虚拟环境。
    • 核心目的是实现项目隔离,让不同项目可以使用不同的 Python 解释器版本及第三方库,互不干扰。
  • 管理规范
    • 该文件夹及其内部内容(如 bin, lib 等)严禁手动移动、删除或修改。
    • 不要向此文件夹内添加任何无关文件。
  • 进阶意义
    • 能够配置并理解虚拟环境,意味着已掌握了开发环境设置这一高门槛步骤,领先了 99% 的初学者。
    • 后续创建新项目时,只需重复相同的步骤即可。

在 PyCharm 中创建并编写 Python 文件

Section titled “在 PyCharm 中创建并编写 Python 文件”
  • 在项目文件夹中创建代码文件:

    1. 在左侧项目目录树中,右键点击项目文件夹。
    2. 选择 新建 (New) > Python 文件 (Python File)
    3. 输入文件名(例如 hello),PyCharm 会自动添加 .py 后缀,回车确认即可进入编辑区域。
  • 实时检查机制:PyCharm 会在编写过程中自动进行语法检查。
    • 若代码存在拼写错误或语法问题,PyCharm 会自动用红色波浪线标记该处,提供即时反馈,帮助开发者在运行前修正错误。
  • 编程的本质是给计算机下指令
    • 只要拥有权限且指令符合计算机的逻辑规则,计算机就会严格按照编写的逻辑执行任务。
  • 使用 print() 函数可以在屏幕上显示指定的文本内容。
    • 语法示例:print("Dad!!")
  • 函数组成部分:
    • print:代表打印(输出)指令。
    • ():圆括号用于存放需要打印的具体内容。
      • 若内容置于括号外,程序将无法识别并可能报错。
    • "":引号用于包裹字符串,标识这是需要打印的文本。
  • 注意事项:
    • 必须使用英文半角括号和引号,使用中文全角符号会导致程序无法运行。
    • 编程语言对字符敏感,大小写和符号格式需严格遵循规范。
  • 编程语言仅识别英文半角标点,使用中文全角符号会导致程序无法运行。

  • 中英文标点在视觉上非常相似,需特别注意区分:

    | 符号类型 | 中文全角 | 英文半角 |

    | –––– | –––– | –––– |

    | 括号 | () | () |

    | 逗号 | , | , |

    | 分号 | ; | ; |

    | 方括号 | [] | [] |

  • 编程时应及时切换输入法至英文模式。

  • print() 函数中,包裹文本内容的引号既可以使用单引号 ' ',也可以使用双引号 " "
  • 引号的作用类似于书面写作中的引用功能,用于明确区分“需要打印的原始文本”与“程序指令”。
  • 无论使用哪种引号,必须确保成对出现,且同样必须为英文半角符号。
  • 字符串定义:被引号包裹的内容(如 "Dad!!")被视为字符串,即一串文本。
  • 引号的作用
    • 明确标识文本内容,防止计算机将其误认为是变量或指令。
    • 即使引号内包含中文或标点符号,程序也能正常处理。
  • 符号规范
    • 必须使用英文半角引号和括号。使用中文全角符号会导致程序无法识别。
    • 即使字符串内包含中文,外层的引号与括号仍需保持英文格式。
  • 数字作为字符串
    • 若数字被引号包裹(如 "666"),计算机会将其视为字符串(文本)而非数值,从而避免计算逻辑错误。
  • 现在实践打印字符串“666”(字符串,非数字)。
  • 双击打开之前创建的项目。
  • 在项目文件夹中编写 print("666")
  • 文件存放位置:选择项目根目录创建Python文件,避免放入venv文件夹,因为venv用于虚拟环境,与代码应分开。
  • 命名规范:文件名避免空格,后缀必须为.py,以便Python解释器识别。
  • 推荐命名:使用英文、数字和下划线(_),这能避免因文件名包含特殊字符或 Emoji 而导致的路径识别问题。
  • 避免使用:特殊符号(如 Emoji、中文字符、空格等),以确保系统在读取文件时不会出现无法定位的问题。
  • Git 初始化弹窗
    • 首次创建文件时,PyCharm 可能会弹出询问是否将文件添加到 Git 版本控制的窗口。
    • 操作建议:在入门阶段,若弹出此窗口,可直接选择“不再询问”并点击“取消”,无需在当前阶段处理 Git 相关配置。
  • 代码执行:在文件内输入代码后,点击回车即可确认创建。
  • 代码实践:在编辑器中输入 print("666") 以完成字符串打印练习。
  • 输入 printpr小写时,PyCharm 提供自动补全提示(显示包含 “pr” 的关键词,如 print)。
  • 完整语法:
print("内容")
- 添加 `()` 和一对 `""`,内写要打印的内容。
  • 运行代码
    • 右键编辑区,选择运行
    • 下方运行窗口显示结果。
  • 程序执行反馈
    • 运行代码后,PyCharm 会在下方运行窗口展示输出结果。
    • 第一行通常显示系统自动触发的运行指令(包含解释器路径)。
    • 最后一行显示退出代码(如 Process finished with exit code 0)。
  • 退出代码含义
    • 0:表示程序运行成功,没有出现错误。
    • 非零数字:表示程序运行过程中出现错误。
  • 调试建议
    • 运行窗口中的系统指令和退出代码由 PyCharm 自动生成,日常编写代码时可忽略,重点关注第一行和最后一行之间的输出内容。
    • 若代码报错,检查重点应放在 print() 函数内部的字符串定义及其符号规范上。
  • 右键运行:选择代码右键运行,成功打印输出。
  • 语法错误触发:使用中文括号(如 print("妈")),编辑器显示红色波浪线实时提醒。
  • 运行反馈:窗口显示红色错误提示(如 SyntaxError),退出代码为非零数字
  • 排查方法:不确定问题时,检查编辑器红色高亮处修正。
  • 字符串可以通过 + 号连接,将多个字符串合并为一个更长的字符串。
  • 拼接逻辑:
print("Hello" + " " + "world" + "!")
- 拼接时,计算机严格按照顺序执行,不会自动添加空格。
- 若要在拼接的词之间保留空格,必须在字符串内部显式包含空格(如 `" "`)。
  • 字符串不仅可以拼接,还支持更复杂的处理方式,如单双引号的嵌套使用(转义)以及处理跨行文本。
  • 灵活运用引号可以避免程序将字符串内容误识别为代码指令。
  • 字符串内容本身包含引号时,需注意内外引号类型匹配。
  • 错误示例print("黑SAT"good")
    • Python 将第一个和第二个引号配对,认为**“黑SAT”**是完整字符串结束。
    • 后续 good 和剩余引号被视为无效代码,导致语法错误(SyntaxError)。
  • 引号嵌套规则
    • 若字符串内部需要包含引号,可通过外层使用不同类型的引号来避免冲突。
    • 例如:print('He said "good!"') 使用外层单引号包裹,内部的双引号即被视为普通字符,不会导致语法错误。
  • 反斜杠转义 (Escape Character)
    • 当字符串内部必须使用与外层相同的引号时,可在该引号前添加反斜杠 ``。
    • 作用:告诉计算机该引号仅为普通字符,而非字符串的结束标志。
    • 示例:
print("He said \"Let's go!\"")
- 在此例中,`\"` 明确表示双引号是文本内容的一部分,防止程序提前终止字符串解析。
  • 打印效果:转义后的字符串原样输出,包括反斜杠后的引号。
    • 示例输出:He said let's go(斜杠后引号保持不变)。
  • 换行规则:Python 逐行读取执行,一句代码不能随意换行。
    • 错误原因:行末未闭合引号或括号,解释器误认为语句中断,导致语法错误。
    • 示例:字符串中直接换行会报错。
  • 若需在字符串中强制换行,可在换行位置插入 \n
    • 示例:print("Hello!\nHi!") 会在输出中将 Hi! 打印在第二行。
  • \n 中的反斜杠是转义符的另一种用法,用于指示后续字符(n)代表换行指令而非普通字母。
  • 当需要打印多行文本(如诗歌)时,使用 \n 或多个 print 语句会显得繁琐。
  • 使用三引号('''""")可以包裹跨行字符串,直接保留代码中的换行格式。
    • 示例:
print('''君不见,黄河之水天上来,
奔流到海不复回。''')
  • 三引号不仅支持跨行,也解决了内部引号嵌套的冲突问题,是处理长文本或复杂格式字符串的推荐方案。
  • 进入实践:在之前项目中新建空白 Python 文件。
  • 示例代码:使用 print(),括号内放置多个字符串,中间用 + 连接。
  • 重点:字符串连接 通过加号实现多字符串合并。
  • 字符串连接的空格处理
    • 拼接字符串时,Python 不会自动添加空格。若需在连接处保留间距,需在字符串内部手动添加空格。
    • 示例:print("你好" + " " + "这是一句代码")
  • 代码中的空格规范
    • 操作符(如 +)两端的空格是可选的,但添加空格可以提高代码的可读性,使排版更美观。
    • 警告:严禁在每行的开头随意添加空格。PyCharm 会通过红色波浪线提示此类缩进错误,因为 Python 对代码缩进有严格的语法要求。
  • 字符串连接时,需手动添加空格以保留间距。例如:print("你好" + " " + "这是一句代码")
  • 操作符(如+)两端空格可选,但可提高可读性。
  • 严禁行首添加空格,会导致缩进错误。
  • 演示:print('Let's go'),PyCharm 会提示错误,因为 ' 被理解为字符串的结束,导致语法错误。
  • 反斜杠转义的应用
    • 当字符串内部需要包含与外层引号相同的引号时,使用反斜杠 `` 进行转义。
    • 示例:print('Let\'s go')
    • 视觉识别:在代码编辑器中,反斜杠和其后的引号通常会显示为相同的颜色,提示它们是一组转义序列。
  • 反斜杠与正斜杠的区别
    • 必须使用反斜杠 \ (位于键盘 Enter 键上方),使用正斜杠 / 将导致代码无法正常运行。
  • print 函数换行
    • 若需在输出中打印两行内容,可以使用两个独立的 print() 语句。
    • 示例:
print('第一行')
print('第二行')
  • print("\n") 输出一个空行。
  • 使用三个单引号 ''' 或三个双引号 """ 实现跨行字符串输入。
  • PyCharm 智能补全:打三个引号后自动匹配。
  • 输入三个引号(‘’’ 或 “”“),PyCharm 自动补全闭合引号。
  • 复制粘贴诗歌等多行文本到三引号间。
  • 运行后,输出完整保留代码中的原始换行格式,原封不动打印。
  • 复制操作:等号左边视为变量名,右边先求值后赋值。
  • 示例场景:MyLove 已存前任手机号,现用信任手机号覆盖但不想丢失旧值。
  • 用新变量(如 MyX)先复制旧值,再更新原变量。
  • 示例:
MyX = MyLove # 先存前任手机号到 MyX
MyLove = "信任的手机号" # 覆盖新值
  • 结果:MyX 保留137那个手机号
  • 频繁使用长字符串(如对象的手机号)很麻烦,难以记忆。
  • 现实类比:通讯录通过人名(标签)找到手机号。
  • 程序变量:同样用于存储或指代值,先给变量取名字。
  • 中间不能有空格,也不能是数字
  • 不能以数字开头:变量名首字符必须是字母或下划线,否则会导致语法错误。
  • 禁止引号包裹:定义变量名时千万不要用引号(单引号或双引号)包裹,否则 Python 会将其识别为字符串而非变量名。
  • 赋值语法:使用等号 = 将右侧的值赋给左侧的变量名。
    • 示例:my_love = "13766666666"
  • 赋值本质:将特定的值存入变量中,后续可通过变量名直接获取该值,无需重复输入数据。
  • 执行顺序:Python 程序从上到下逐行执行。必须先对变量进行赋值,才能在后续代码中调用该变量。
    • 若在赋值前使用变量,程序会报 NameError,如同未将号码存入通讯录却试图拨号一样。
  • 变量复用:赋值后,变量名可被反复调用,极大简化了代码编写与维护工作。
  • 变量的本质:之所以称为“变量”,是因为其存储的值可以根据程序逻辑随时改变。
  • 动态更新示例
my_love = "13766666666" # 初始赋值
my_love = "15066666666" # 赋予新值,原值被覆盖
  • 逻辑应用:当对象更换手机号或更换对象时,只需对原变量名进行新的赋值操作,后续程序中调用该变量时,会自动获取更新后的值。
  • 逻辑复杂时,用print打印当前变量值,了解运行情况。
  • 示例:打招呼语句**“您好吃了吗”** + 名字(如招三)。
  • 拼接后打印:“招三,您好吃了吗”(女士类似)。
  • 当需要多次输出相同字符串(如“您好吃了吗”)时,直接硬编码会造成冗余且难以维护。
  • 通过将重复字符串赋值给变量,可以大幅简化代码并提高灵活性。
greet = "您好吃了吗"
print(greet + "张三")
print(greet + "李四")
  • 这种方式下,若需要修改问候语,只需修改 greet 变量的值,所有后续的 print 语句都会自动更新,避免了逐行修改的繁琐。

变量在字符串拼接中的动态更新

Section titled “变量在字符串拼接中的动态更新”
  • 批量修改的便捷性:通过将重复的问候语赋值给变量(如 greet),当需要修改问候语(例如从中文“您好吃了吗”改为英文“Yo what’s up”)时,只需在代码顶部修改一次变量值。
  • 自动联动:程序运行时,所有引用该变量的 print 语句会自动获取更新后的值,无需逐行手动修改。
greet = "Yo what's up"
print(greet + "张三")
print(greet + "李四")
print(greet + "王五")
  • 变量的动态本质:变量之所以称为“变量”,是因为其存储的值可以根据程序逻辑随时改变,后续所有调用该变量的地方都会同步反映出最新的赋值结果。
  • 问题:直接替换变量值会导致原值丢失,无法找回之前的值(如中文问候)。
  • 解决:先定义 groupchinese = group,备份旧值;再定义 groupenglish = "新的值",存入新值。
groupchinese = group # 备份之前的中文 group
groupenglish = "新的值" # 英文新值
  • 后续可分别使用不同变量调用对应值。
  • grid 更新为 gridenglish,打印查看:现在所有的打招呼均为英文
  • 同时保留 grid chinese,需切换回中文打招呼时,直接使用即可。
grid = gridenglish
print(grid) # 现在所有的打招呼应该还是英文的
  • 禁止数字开头:变量名首字符必须为字母或下划线,以数字开头会导致语法错误。
  • Python 语法校验:若违反命名规则,Python 解释器会直接报错。
  • 可读性优先:变量名应具备语义,便于理解和记忆,避免使用 aa1abc 等无意义命名。
  • 避免拼音命名:不建议使用拼音作为变量名,因为拼音阅读速度慢且容易引起歧义。
  • 命名原则:好的变量名应能直观反映其存储数据的含义,避免在未来维护代码时忘记变量用途。
  • 避免使用拼音命名
    • 拼音阅读速度慢,且容易引起歧义(如 pingyinpinyinpingying 的混淆)。
    • 拼音不带声调容易产生歧义(如 gaoji 既可以是“高级”也可以是“搞基”),导致代码意图不明。
  • 变量命名的语义化
    • 变量名应直观反映存储数据的含义,避免使用 aa1abc 等无意义命名,防止维护时遗忘用途。
  • 版本差异
    • Python 3.0 版本之后开始支持中文变量名。
    • 尽管支持,但主流开发习惯仍倾向于使用英文单词作为变量名,以保持代码的通用性和可读性。
  • 编码兼容性风险
    • 若控制台或日志系统的编码格式不支持中文,使用中文命名变量会导致乱码。
    • 即使语言本身支持中文变量名,也应考虑运行环境的兼容性。
  • 输入法切换成本
    • 由于代码关键字和符号均为英文,频繁在中文和英文输入法之间切换会降低编码效率。
  • 翻译工具的普及
    • 使用翻译工具(如词典)将中文含义转为英文命名并不困难,因此保持英文命名的习惯在工程实践中更为普遍。

Python 变量命名规范:下划线命名法 (snake_case)

Section titled “Python 变量命名规范:下划线命名法 (snake_case)”
  • 约定俗成:Python 社区普遍采用下划线命名法来增加多单词变量的可读性。

  • 核心规则

    1. 字母全部小写。
    2. 不同单词之间使用下划线 _ 分隔。
  • 示例

    • user_age (用户年龄)
    • user_gender (用户性别)
  • 设计逻辑:下划线替代了空格,既满足了变量名不能包含空格的语法限制,又保持了语义的清晰度。

  • 大小写区分:Python 变量名是区分大小写的,user_ageuser_Age 被视为两个完全不同的变量。
  • 关键字冲突:变量名不能使用 Python 的内置关键字(如 print)。
  • 后果:如果将 print 赋值为字符串,print 函数原有的打印功能会被覆盖,导致后续无法再调用该函数进行输出。
print = "u123" # 错误做法:覆盖了内置函数
print("Hello") # 报错:此时 print 已不再是函数
  • Python 有30几个关键字,这些后面都会成为很眼熟的词,不需要现在死记。
  • PyCharm 会对 Python 关键字 进行颜色高亮展示。
  • 如果打印出来的变量名是彩色的,说明那是关键字,最好换成其他的。
  • 在代码文件开头使用 import map 引入模块,以调用其函数。
import map
  • 格式:map.sqrt(),置于最开头后跟点号调用平方根函数。
  • 将分数线以上内容全部括起来,除以 2 * a
(map.sqrt(...)) / (2 * a)

求解公式实践:第一个解与第二个解

Section titled “求解公式实践:第一个解与第二个解”

第一个解与第二个解的快速修改

Section titled “第一个解与第二个解的快速修改”
  • 第二个解修改:只需将公式中的加法改为减法
  • 无 print 无输出:仅运算不会显示结果,必须用 print() 包裹整个运算
  • 运行步骤:包裹后点击运行,即可看到两个正确结果
  • 其他方程时,无需更改公式
import map
print((-b + map.sqrt(b**2 - 4*a*c)) / (2*a)) # 第一个解
print((-b - map.sqrt(b**2 - 4*a*c)) / (2*a)) # 第二个解
  • 公式可视化与检查:将完整公式写入代码中,便于随时查看逻辑结构,避免手动计算时的拼写或录入错误。
  • 变量动态更新:当需要计算不同方程时,只需修改变量 a, b, c 的值,无需重写整个公式,极大提高了复用性。
  • 高效调试:通过代码运行直接获取计算结果,减少了人工计算的繁琐步骤,且能快速验证不同参数下的解。
  • 公式复杂时,可通过中间变量分步骤计算,提高代码清晰度。
  • 示例:将二次方程判别式 b² - 4ac 提取为变量 delta
import map
delta = b**2 - 4 * a * c
print((-b + map.sqrt(delta)) / (2 * a))
print((-b - map.sqrt(delta)) / (2 * a))
  • 优势:过程更简洁清晰,便于阅读、维护与调试。
  • 注释不影响程序执行,有些程序员放飞自我:求佛祖保佑无bug、血泪劝退其他程序员。
  • 17年加米音乐:程序员注释免费VIP用户为**“穷逼VIP”**,引发用户不满。
  • 程序员道歉:今后会**“正正经经写注释”**。
  • 不要在注释里过多**“放飞自我”**。
  • 编程世界的数据种类称为数据类型
  • 包括:字符串整数浮点数画类型空置类型,以及后续的列表词典等。
  • 一串字符,表示文本内容。
  • 双引号单引号包裹。
  • 使用列函数获取字符串长度。
  • 空格、数字、符号等各占一个长度
  • 转义符特殊:如反斜杠后跟N(换行),整个转义符占一个长度
  • 字符串后加方括号 [],放入索引提取单个字符。
  • 索引从0开始:第一个为0,排第四的用索引3,返回 'L' 字符。
  • 整数在Python叫 int
  • 辅典数在Python叫 flump
  • 操作已在之前数学运算覆盖,直观不赘述。
  • 只包含两种值:true(t大写)和 false(f大写)。
  • 专门用于表示“完全没有值”的特殊数据类型。
  • 仅包含一个值:None
  • 注意事项:None 不等于 0,不等于空字符串 "",也不等于 False
  • 适用场景:当需要定义一个变量但暂时不知道其具体值时,可先将其赋值为 None
  • 使用 type() 函数可以查看任何对象的数据类型。
  • 示例输出:
type("Hello") # <class 'str'>
type(6) # <class 'int'>
type(6.0) # <class 'float'>
type(True) # <class 'bool'>
type(None) # <class 'NoneType'>
  • 数据类型决定了该对象可以调用哪些函数。
  • 函数是执行特定功能的工具,必须传入正确类型的数据才能得到预期的输出(类比:巧克力机器只能处理可可粉,若输入石头则会报错)。
  • 示例:len() 函数适用于字符串,若传入不匹配的类型则无法正常工作。

len() 函数类型限制与字符串演示

Section titled “len() 函数类型限制与字符串演示”
  • len() 函数对整数浮点数使用会报错。
  • 字符串有效。
  • 定义变量:hello = "Hello"
  • 使用 print(len(hello)) 输出结果:12
  • 原因:空格符号各占一个长度
  • 获取第一个字符:s[0] 返回 'H'
  • 字符串长度为12,最后一个索引为11
  • s[11] 返回 '!'(感叹号)。
  • 使用 len(s) - 1 计算最后一个索引,结果相同。
  • 示例代码:
s = "Hello world!" # 长度12
print(s[0]) # 'H'
print(s[11]) # '!'
print(s[len(s)-1]) # '!'
  • 赋值 tree1 = Truetree2 = True:需首字母大写
  • 小写 true 会报错,不被识别为布尔值。
  • 加引号 true 则成字符串
  • n = None首字母大写
  • 小写 none 不被识别。
  • 通过赋值实践确认数据类型正确性。
  • type(None) 输出 <class 'NoneType'>
  • type(1.5) 输出 <class 'float'>,因小数点而非 int
  • 对布尔值使用 len()TypeError
  • 示例:
print(len(True)) # TypeError
  • 强调了解对象类型避免错误。
  • 命名行模式:
    • 保存并运行整个文件。
    • Python 解释器逐行解析和执行文件。
  • 交互模式:
    • Python 逐行执行命令并展示结果。
    • 无需创建新文件。
    • 可通过 PyCharm 下方的 Python 控制台,或 Windows 的 CMD,Mac 的终端(输入 python3)进入。
    • 出现 >>> 提示符,表示等待指令。
    • 输入 Python 命令后立即执行,并显示结果。
    • 例如:print("hi") 会立即输出 hi
  • 无需创建 Python 文件即可直接运行代码。
  • 无需显式调用 print() 函数即可查看结果,Python 会自动返回表达式的计算结果。
  • 示例:
>>> a = "你好" + "!"
>>> a
'你好!'
>>> 3 * 2
6
  • 使用 quit() 函数(需带括号)。
  • 使用快捷键 Ctrl + D
  • 交互模式:适合快速测试和验证代码,但输入的指令不会被保存。
  • 命令行模式(脚本运行):适合编写正式程序,代码可保存并重复执行,是我们日常开发的主要方式。
  • 比喻游乐场:随意测试命令、查看输出结果。
  • 适合一次性计算:输入算式得结果后直接退出。
  • 优势:快速计算,显得行云流水
  • 使用 input() 实现简单身体质量指数计算器。
  • 公式:$$BMI = \frac{weight}{height^2}$$
    • weight千克 (kg)。
    • height (m)。
  • 首先定义变量 use_weight
  • userweight = input("请输入您的体重单位是千克")
  • userheight = input("请输入您的身高单位是米")
  • userbmi = userweight / userheight ** 2
  • PyCharm 提示问题:userweightuserheight字符串(str)。
  • 字符串不支持除法(/)和平方(**)运算。
  • 先将两者转换为数字类型才能计算。
  • 身高暂不使用 int() 函数转换。
  • float() 函数将字符串转换为浮点数。
  • 示例:计算 BMI 前转换 heightweight 输入。
  • 计算 BMI 值:bmi = weight / (height ** 2)
  • 计算结果为数字,打印前需用 str() 转字符串。
  • 示例:
height_float = float(height_str)
weight_float = float(weight_str)
bmi = weight_float / (height_float ** 2)
print('BMI: ' + str(bmi))
  • 输入任意身高、体重数字,运行成功。
  • 实现任意输入的 BMI 计算器。
  • 条件语句的核心在于根据判断条件是否满足来决定执行的内容。
  • 现实生活中的决策逻辑示例: - 情绪决定行为:根据女朋友心情判断是否能打游戏。 - 职场决策:根据老板脸色判断是否提加工资。
  • 基本语法结构:
if [条件]:

else:

- 条件判断的本质是布尔值True False的求值
- 只要表达式的结果为布尔值即可直接作为判断条件
  • 可以直接将布尔变量放置在 if 后面作为判断条件:
is_happy = True
if is_happy:
  • 使用比较运算符可以生成布尔值,常见的比较运算符包括等于号 ==
  • 比较运算符用于判断两个对象的值是否相等:
    • 3 == 3 返回 True
    • "a" == "b" 返回 False
  • 比较运算符用于判断两个对象的关系,返回布尔值(TrueFalse)。
  • 等于判断使用双等号 ==,避免与赋值运算符 = 混淆。
  • 不等于判断使用 !=
  • 常用运算符包括:
    • 大于 >
    • 大于等于 >=
    • 小于 <
    • 小于等于 <=
  • if 语句用于根据条件执行代码块,语法结构如下:
if [条件]:
[执行语句]
  • 语法注意事项
    • 条件后必须紧跟冒号 :,表示条件定义结束。
    • 执行语句必须进行缩进(推荐使用 4 个空格),否则会报错。
    • 若条件为真,则执行缩进内的所有代码块。
    • 若条件为假,则跳过该代码块。
  • 示例:if 5 >= 20: 结果为 False,因此内部代码不会被执行。

条件判断的潜在问题与数据类型转换

Section titled “条件判断的潜在问题与数据类型转换”
  • if 语句中使用 input() 获取的值进行判断时,必须注意 input() 返回的始终是字符串类型。
  • 若要将输入的字符串(如 mood_index)与数字进行比较,必须先用 int()float() 函数将其转换为数值类型,否则逻辑判断将无法正确执行。
  • else 语句是可选的,用于处理条件为 False 时的逻辑。
  • 语法要点:
    • else 必须紧跟冒号 :
    • 内部执行语句必须保持缩进(即使 PyCharm 会自动缩进,编写时仍需注意)。
mood_index = int(input("你今天的心情指数是多少?"))
if mood_index >= 60:
print("恭喜,今晚可以打游戏,去吧皮卡丘!")
else:
print("心情不佳,建议休息。")
  • 代码块可以包含多行语句,只要保持一致的缩进即可。
  • else 语句是可选的,根据逻辑需求决定是否需要处理条件为 False 的情况。
  • 缩进是 Python 识别代码块归属的唯一方式,必须严格遵守。
  • 每一行属于 ifelse 的代码都必须有缩进,否则程序无法正确识别逻辑层级。
  • print 语句中,可以通过拼接字符串和变量来输出更丰富的提示信息。
  • 示例:在 print 中加入表情符号或动态文本,增强程序运行时的反馈感。

示例:多行代码块与表情符号输出

Section titled “示例:多行代码块与表情符号输出”

if mood_index >= 60:

print("恭喜,今晚可以打游戏!")
print("( ^\_^) / ")

else:

print("为了自个小命,还是别打了。")
  • 条件语句后若无缩进,视为 if 块结束,该 print 语句总是执行
  • 示例:输入后第一句和第二句都会打印
  • else 不能独立存在,必须依附于 if
  • 缺少对应 if 会导致Python 报错
  • 正确缩进后,输入“佐识”可见第二句也被打印
  • 确认 if-else 逻辑正常工作。
  • 条件为:执行 if 下语句,跳过 else。
  • 条件为:执行 else 下语句。
  • 单条件不足以决定行动。
  • 示例:女友心情不好时,不敢屁股对着她打一晚上游戏,需结合其他条件。
  • 当需要在满足第一层条件的前提下,进一步进行逻辑判断时,可以使用嵌套 if 语句。
  • 逻辑流程
if [条件1]:
if [条件2]:
[语句A]
else:
[语句B]
  • 执行前提:只有当 条件1 为真时,程序才会进入内部判断 条件2;若 条件1 为假,则直接跳过整个嵌套代码块。
  • 嵌套在内部的 ifelse 语句必须在外部 if 的基础上再次缩进(通常再增加 4 个空格)。
  • 缩进是 Python 识别代码层级的唯一依据,错误的缩进会导致逻辑判断范围错误或语法报错。
[开始]
|
[条件1] --(为假)--> [跳过]
|
(为真)
|
[条件2] --(为真)--> [语句A]
|
(为假)
|
[语句B]
  • 当嵌套 if-else 结构过于复杂时,可以使用 elif (else if) 关键字来实现多条件分支,使逻辑更加清晰直观。
  • elif 允许在多个条件中进行选择,Python 会从上到下依次判断,一旦找到第一个为 True 的条件,即执行对应的语句块并跳过后续所有分支。
if [条件1]:
[语句A]
elif [条件2]:
[语句B]
elif [条件3]:
[语句C]
else:
[语句D]
  • 执行逻辑
    • 顺序执行:Python 按照代码编写顺序从上往下检查条件。
    • 唯一性:一旦某个 ifelif 条件满足,后续的 elifelse 将不再执行。
    • 灵活性:elif 的数量没有限制,可以根据业务需求添加任意多个。
  • 与嵌套结构的对比
    • elif 结构扁平化,避免了过深的缩进(即“嵌套地狱”),提高了代码的可读性和可维护性。

多条件 elif 在 BMI 计算器中的应用

Section titled “多条件 elif 在 BMI 计算器中的应用”
  • Python 从上到下依次检查 ifelif 条件。
  • 一旦某个条件为 True,执行对应代码块,跳过后续所有 elifelse
  • 示例:条件2和条件3同时满足,只执行条件2分支,后续判断停止。
  • 基于之前代码(输入体重/身高,转浮点数,计算 BMI = 体重 / 身高²)。
  • 成人BMI标准:
    • if bmi <= 18.5: 偏瘦
    • elif bmi > 18.5 and bmi <= 25: 正常
    • elif bmi > 25 and bmi <= 30: 超重
  • 使用 elif 实现多级分类,提高逻辑清晰度。
  • 逻辑结构:通过 if 和多个 elif 组合,可以清晰地对 BMI 数值进行分段判断。
  • 示例代码:
if user_BMI <= 18.5:
print("BMI值属于偏瘦范围")
elif 18.5 < user_BMI <= 25:
print("BMI值属于正常范围")
elif 25 < user_BMI <= 30:
print("BMI值属于超重范围")
else:
print("BMI值属于肥胖范围")
  • Python 支持直接使用连等式表示范围,例如 18.5 < user_BMI <= 25,这与数学中的写法一致。
  • 这种写法比使用 and 连接两个比较表达式更加简洁直观,且在 Python 中完全合法。
  • 使用 if-elif-else 结构对计算出的 user_BMI 进行多级分类判断。
  • Python 支持数学连等式语法,使范围判断更简洁直观。
if user_BMI <= 18.5:
print("此BMI值属于偏瘦范围")
elif 18.5 < user_BMI <= 25:
print("此BMI值属于正常范围")
elif 25 < user_BMI <= 30:
print("此BMI值属于超重范围")
else:
print("此BMI值属于肥胖范围")
  • 运行程序后,输入体重(kg)和身高(m)即可获得对应的 BMI 分类。
  • 进一步优化的思路:
    • 使用嵌套逻辑根据用户性别输出个性化问候(如“先生您好”或“女士您好”)。
    • 增加输入校验,确保输入的数值合理,避免负数或零导致计算错误。

嵌套条件语句的局限与逻辑运算引入

Section titled “嵌套条件语句的局限与逻辑运算引入”

双条件嵌套示例:心情与在家判断

Section titled “双条件嵌套示例:心情与在家判断”
  • 对象心情指数 < 60 时,进一步判断是否在家
    • 在家(真):不能打游戏
    • 不在家(假):拥有自由
if 心情指数 < 60:
if 在家:
print("不能打游戏")
else:
print("拥有自由")
  • 示例:奖励 switch 新年礼物需满足 4 个条件
    • 家务次数 >10 次
    • 翻红包次数 >1 次
    • 陪逛街次数 >4 次
    • 一个月内没有惹生气
  • 用嵌套 if 表达:缩进层级过多,代码不优雅,易迷失逻辑(嵌套地狱)。
  • 复杂多条件场景下,逻辑运算符 可简化嵌套结构,提高代码可读性。
  • Python 仅包含三个逻辑运算符:andornot
  • 逻辑运算符功能
    • and (与):连接两个或以上操作对象,仅当所有条件均为 True 时返回 True;只要有一个为 False,结果即为 False
    • or (或):连接两个或以上操作对象,只要有一个条件为 True,即返回 True;仅当所有条件均为 False 时返回 False
    • not (非):对单个操作对象取反,若原值为 True 则返回 False,反之亦然。
  • and 示例:x > 5 and x < 10 (表示 x 同时满足大于 5 且小于 10)。
  • or 示例:x > 5 or x < 10 (表示 x 满足大于 5 或小于 10 其中之一)。
  • not 示例:not x > 5 (表示 x 不大于 5)。
  • 逻辑运算支持链式连接多个判断条件,例如:
x > 5 and x < 10 and x * 2 == 12
  • Python 会依次计算每个表达式的布尔值,并根据逻辑运算符的规则得出最终结果。
  • not 运算符优先级较高,通常用于对单个布尔结果进行翻转。
  • 为了简化复杂的嵌套判断,可以将逻辑运算符类比为人的情绪倾向:
    • and (与):悲观者。只要有一个条件为 False (不开心),整个结果就为 False
    • or (或):乐观者。只要有一个条件为 True (开心),整个结果就为 True
    • not (非):反转者。直接将布尔值 True 变为 False,反之亦然。
  • 当多种逻辑运算符混合使用时,遵循特定的优先级,类似于数学中的运算顺序:
    • 优先级顺序:not > and > or
  • 使用括号改变优先级
    • 与数学运算中的括号用法一致,可以使用 () 强制改变运算顺序,确保逻辑按预期执行。
    • 示例:not (x > 5 and (x < 10 or x == 12))

逻辑运算符在实际业务中的应用

Section titled “逻辑运算符在实际业务中的应用”
  • 将多个条件合并到一个 if 语句中,可以显著提升代码的可读性和优雅度,避免“嵌套地狱”。
  • 示例:奖励 Switch 礼物的复杂条件判断
if (house_work_count > 10 and
red_envelope_count > 1 and
shopping_count > 4 and
not has_been_angry):
print("摩拳擦掌等待Switch!")
else:
print("Switch随风散去...")
  • 注意:虽然逻辑运算通常比嵌套更优雅,但并非所有场景都适用,需根据具体业务逻辑选择最清晰的表达方式。
  • 当需要处理大量关联数据时,使用多个独立变量(如 item1, item2…)会导致代码冗余且难以维护。
  • 数据结构:列表(List)是一种将相关联数据整合在一起的容器,能优雅地解决变量过多的问题。
  • 创建列表:使用一对中括号 [] 表示,空列表即 []
  • 添加元素:使用 append() 方法向已定义的列表中追加新数据。

shopping_list = [“键盘”, “键帽”]

shopping_list.append(“显示器”)

  • append() 是一种“方法”,它与函数类似,专门负责执行某个特定的功能。在列表对象后通过点号 . 调用,体现了面向对象的调用习惯。
  • 方法 (Method):通常在对象后通过点号 . 调用,例如 shopping_list.append("显示器")。这种方式强调该功能是专门为特定对象类型(如列表)设计的。
  • 函数 (Function):通过函数名直接调用,并将操作对象作为参数传入,例如 len(shopping_list)
  • 可变类型 (Mutable):如列表 list。修改其内容时,直接在原对象上进行操作,不需要重新赋值。
  • 不可变类型 (Immutable):如字符串 str、整数 int、浮点数 float、布尔值 bool。对这些类型进行操作(如 s.upper())时,原对象保持不变,必须将返回的新值重新赋值给变量才能保存修改。
s = "Hello"

s.upper() 返回 “HELLO”,但 s 本身仍为 “Hello”

Section titled “s.upper() 返回 “HELLO”,但 s 本身仍为 “Hello””

print(s.upper())

print(s)

s = s.upper()

shopping_list = ["键盘"]

append 直接修改原列表,不需要重新赋值

Section titled “append 直接修改原列表,不需要重新赋值”

shopping_list.append(“显示器”)

print(shopping_list)

  • 使用 remove() 方法可以删除列表中的指定元素。
  • 调用方式:list_name.remove(value)
  • 注意事项:
    • remove() 直接作用于原列表,无需重新赋值。
    • 若尝试删除列表中不存在的元素,程序会报错。
  • Python 列表可以存储不同数据类型的元素(如字符串、浮点数、布尔值、None 等),这与其他语言有显著区别,使用起来非常灵活。
  • 使用 len() 函数可以获取列表的长度(即元素个数),与获取字符串长度的方法一致。
  • 列表通过索引访问元素,索引从 0 开始。
  • 获取最后一个元素的索引公式为:列表长度 - 1
  • 示例:list[0] 获取第一个元素。
  • 可以通过索引直接对列表中的某个位置进行重新赋值以修改内容。
  • 列表是可变的,可以直接通过索引定位并重新赋值来修改特定位置的元素。
  • 这种操作会直接覆盖该位置原本的元素,不需要重新创建列表。
shopping_list = ["键盘", "显示器", "硬盘"]
shopping_list[1] = "音响" # 索引为1的元素被更改
print(shopping_list)

输出: [“键盘”, “音响”, “硬盘”]

Section titled “输出: [“键盘”, “音响”, “硬盘”]”
  • Python 提供了许多针对列表的内置函数,用于快速处理数据:
    • max(list):返回列表中的最大值。
    • min(list):返回列表中的最小值。
    • sorted(list):返回一个排序后的新列表,且不会改变原列表的顺序
num_list = [1, 13, -7, 2, 96]
print(max(num_list)) # 96
print(min(num_list)) # -7
print(sorted(num_list)) # [-7, 1, 2, 13, 96]
  • 创建空列表:使用 []
  • 动态添加元素:使用 append() 方法。
shopping_list = []
shopping_list.append("键盘")
shopping_list.append("键帽")
print(shopping_list)
  • shopping_list 中删除 键帽shopping_list.remove("键帽")
  • 删除后打印列表,确认键帽已不在其中。
shopping_list.append("音响")
shopping_list.append("电竞椅")
  • print(len(shopping_list)) 输出 3,确认元素个数。
  • 访问第一个元素:print(shopping_list[0]) 输出 键盘
print(len(shopping_list)) # 3
print(shopping_list[0]) # 键盘
  • 将索引 1 的元素从 音响 更新为 硬盘
  • 打印验证:原 音响硬盘 覆盖。
shopping_list[1] = "硬盘"
print(shopping_list) # ["键盘", "硬盘", "..."]
  • 定义价格列表:键盘=799显示器=1024硬盘=200电机=800
price_list = [799, 1024, 200, 800]
  • max(price_list) 获取最大值:1024
  • min(price_list) 获取最小值:200
  • sorted(price_list) 返回从小到大排序的新列表,不修改原列表。

列表内置函数输出演示与通讯录变量实现

Section titled “列表内置函数输出演示与通讯录变量实现”
  • max() 输出:最大价格
  • min() 输出:最小价格
  • sorted() 输出:排序后的新列表
  • 特定打印:成功输出 Wabbs
  • 通讯录需求:通过姓名查找手机号。
  • 用变量实现:
    • 小名电话 = '手机号值'
    • 小花电话 = '手机号值'
  • 查找方式:直接用变量名访问对应电话。
  • 使用独立变量(如 小名电话小花电话)存储通讯录虽然可行,但存在扩展性问题:
    • 随着联系人增加,变量命名和管理变得繁琐。
    • 无法通过统一的逻辑(如循环)批量处理联系人信息。
    • 无法将姓名与电话建立直观的映射关系。
  • 核心概念:字典是一种用于存储键值对 (Key-Value Pair) 的数据结构,专门用于解决“通过名称查找对应数据”的需求。
  • 映射逻辑
    • 键 (Key):相当于索引或名称(如“小明”),用于唯一标识和查找。
    • 值 (Value):相当于具体存储的数据(如“13700000000”)。
  • 类比:类似于现实生活中的字典,通过“字”(键)快速查找到对应的“释义”(值)。
  • 定义空字典:使用花括号 {} 表示。
  • 存储结构{键: 值}
  • 查找机制:通过键直接访问对应的值,比遍历列表更高效且直观。

contacts = {

"小明": "13700000000",
"小花": "13700000001"

}

  • 定义字典:使用花括号 {} 包含键值对,键与值之间用冒号 : 分隔,键值对之间用逗号 , 分隔。
contacts = {"小明": "13700000000", "小花": "13700000001"}
  • 查找值:通过在字典名后使用方括号 [] 放入键来获取对应的值。
print(contacts["小明"]) # 输出: 13700000000
  • 键的类型要求:字典的键必须是不可变 (Immutable) 类型。
    • 可作为键的类型:字符串 (str)、整数 (int)、浮点数 (float)、布尔值 (bool)、元组 (tuple)。
    • 不可作为键的类型:列表 (list) 等可变数据类型。
  • 应用场景:当需要用多个维度(如姓名和年龄)唯一标识一个人时,若直接使用列表作为键会报错,此时可以使用元组作为键,因为元组是不可变的。

contacts = {[“张伟”, 23]: “15000000000”} # 报错

Section titled “contacts = {[“张伟”, 23]: “15000000000”} # 报错”
contacts = {("张伟", 23): "15000000000"}
  • 定义:元组是不可变的序列,使用圆括号 () 定义。
  • 与列表的区别
    • 语法:列表用 [],元组用 ()
    • 可变性:列表可修改(添加/删除/更新),元组不可修改。
    • 方法支持:元组不支持 append()remove() 等修改操作,因此在需要保证数据结构固定不变时非常有用。
  • 字典的动态修改
    • 字典与列表一样是可变的,支持添加新键值对和更新已有键的值。
    • 添加/更新语法字典名[键] = 值
    • 若键已存在,该操作会覆盖原有值;若键不存在,则新增该键值对。

contacts = {“小明”: “13700000000”, “小花”: “13700000001”}

contacts[“美女A”] = “18600000000” # 添加

contacts[“美女A”] = “18600000000” # 若已存在则覆盖

- **键的存在性检查**
- 使用 `in` 关键字检查某个键是否存在于字典中返回布尔值
```python
if "小明" in contacts:
print("存在")
  • 删除键值对
    • 使用 del 关键字配合方括号索引删除指定键。
del contacts["小明"]
  • len() 函数适用于字典,返回键值对数量
print(len(contacts)) # 输出键值对个数
  • 使用 del 字典名[键] 删除指定键及其值。
  • 注意:若键不存在,会报错。
  • 需求:查询网络流行语含义。
  • 结合字典、input()if 判断实现。
  • 步骤
    • 创建空字典 {} 或预置词条:{键: 值}
    • 网上搜索流行语词条,复制添加(如第一个词条,下一个“剑职队用豆豪”)。
  • 长词条换行建议:较长内容建议换行,每个词条间更清晰,仍用键:值格式。
  • 创建初始字典,长度为2
  • 添加方法字典名[键] = 值,依次添加后续流行语(如流星域)及其含义。
  • 添加后字典有3条内容。
  • 通过剪辑扩展至10条内容。
  • 准备开始查询功能。
  • 获取用户输入
    • 使用 input() 函数获取用户想要查询的词条,并将其赋值给变量 query
    • 注意:必须将输入结果存入变量,否则无法获取用户输入的值。
  • 查询逻辑实现
    • 使用 if 语句结合 in 运算符判断查询的词条是否存在于字典中。
    • 若存在,打印查询结果;若不存在,可根据需求后续扩展提示逻辑。
  • 代码实现示例
query = input("请输入您想要查询的词条:")
if query in slang_dict:
print("您查询的" + query + "含义如下:")
print(slang_dict[query])
  • 获取字典值
    • 通过 字典名[键] 的方式访问并获取对应的值。

电子词典:异常处理与功能扩展

Section titled “电子词典:异常处理与功能扩展”
  • 查询失败提示
    • 若用户查询的词条不在字典中,应使用 else 语句给出明确反馈,避免程序无响应。
    • 示例:print("您查询的流行语暂未收录")
  • 获取字典大小
    • 使用 len() 函数获取字典中键值对的总数。
  • 类型转换注意事项
    • len() 返回的是整数(int),若要将其拼接在字符串中进行打印,必须先使用 str() 函数将其转换为字符串。
    • 拼接示例:print("当前词典收录词条数为:" + str(len(slang_dict)))
query = input("请输入您想要查询的词条:")
if query in slang_dict:
print("您查询的" + query + "含义如下:")
print(slang_dict[query])
else:
print("您查询的流行语暂未收录")
print("当前词典收录词条数为:" + str(len(slang_dict)))
  • 成功查询:输入 webds,打印其含义(字典中存在)。
  • 失败查询:输入不存在词条(如 ),打印“您查询的流行语暂未收录”。
  • 结束输出:打印当前词典收录词条总数。

大数据场景:全公司上万人体温报备,逐条查找高于38度体温低效。

  • 用途:处理大量数据,实现迭代(逐个遍历)。
  • 迭代对象:列表、字典、字符串。
  • 核心语法
for 变量名 in 可迭代对象:

- **执行机制**
- `for` 关键字后跟自定义变量名该变量在循环中依次被赋值为可迭代对象如列表字典字符串中的每一个元素
- 循环体内的代码必须缩进会针对每一个元素执行一次
- **变量命名**
- 循环变量名可自定义建议根据业务含义命名 `temperature`),以提高代码可读性
  • 场景:遍历包含全公司人员体温的列表,对每个体温数值进行检查。
  • 代码实现
temperature_list = [36.4, 36.6, 36.2, 37.0, 36.8]
for temperature in temperature_list:

在此编写检查逻辑,例如 if temperature > 38:

Section titled “在此编写检查逻辑,例如 if temperature > 38:”
- **缩进规则**所有在 `for` 下方缩进的代码均被视为循环体的一部分对列表中的每个元素都会执行一遍
  • 代码实现示例
temperature_list = [36.4, 36.6, 36.2, 37.0, 36.5, 38.3, 38.1]
for temperature in temperature_list:
if temperature >= 38:
print(temperature)
print("完球了")
  • 逻辑分析
    • 该循环遍历列表中的每个体温数值,当数值大于或等于38时,触发打印逻辑。
    • 这种方式能快速统计有多少人发烧,但无法直接关联到具体的工号。

字典与循环的结合:处理键值对

Section titled “字典与循环的结合:处理键值对”
  • 场景:当需要关联工号(键)与体温(值)时,单纯的列表循环不足以满足需求。
  • 字典方法
    • dict.keys():获取所有键。
    • dict.values():获取所有值。
    • dict.items():获取所有键值对(元组形式)。
  • 结合 for 循环遍历字典
temperature_dict = {"111": 36.4, "112": 36.6, "113": 38.3}
for staff_id, temperature in temperature_dict.items():
if temperature >= 38:
print(staff_id)
  • 解包机制 (Unpacking)
    • for 循环后跟两个变量时,Python 会自动将 items() 返回的元组中的第一个元素赋给 staff_id,第二个元素赋给 temperature
    • 这等同于手动解包:staff_id = temperature_tuple[0]temperature = temperature_tuple[1]
  • range 函数:用于生成一个整数序列,常与 for 循环配合实现指定次数的重复操作。
    • 语法range(起始值, 结束值)
    • 注意:生成的序列包含起始值,但不包含结束值(左闭右开)。
    • 示例range(5, 10) 会生成 5, 6, 7, 8, 9。
  • 语法range(起始值, 结束值, 步长)
    • 步长:决定了每次迭代跨越的数值间隔。
    • 默认值:若不指定步长,默认为 1。
    • 示例range(1, 10, 2) 会生成 1, 3, 5, 7, 9。
  • 高斯求和案例:计算 1 到 100 的和,利用公式 (首项 + 尾项) * 项数 / 2 可快速得出 5050。
  • 代码实现:相比手动累加,使用循环处理大规模数值计算更高效且不易出错,体现了编程在处理重复逻辑时的核心价值。
  • 代码实现逻辑
    • 初始化一个变量 total = 0 用于累加。
    • 使用 for i in range(1, 101) 遍历 1 到 100 的整数。
    • 在循环体中执行 total = total + i,将当前数值累加到 total 中。
    • 循环结束后,total 即为 1 到 100 的求和结果(5050)。
total = 0
for i in range(1, 101):
total = total + i
print(total)
  • 编程解决数学问题的优势
    • 相比手动计算或公式推导,编程通过循环处理重复累加逻辑,能够快速、准确地解决大规模数值计算问题,体现了计算机处理重复性任务的高效性。

while 循环:处理未知次数的重复任务

Section titled “while 循环:处理未知次数的重复任务”
  • 局限性for 循环通常用于已知次数或已知集合的遍历。当任务需要持续执行直到某个条件不再满足(例如等待日落),而无法预知确切次数时,for 循环不再适用。
  • while 循环的引入:在无法确定循环次数的情况下,while 循环能够持续运行,直到条件变为 False
  • 语法
while 条件:
行动
  • 执行逻辑
    • 计算机首先判断 条件 是否为真(True)。
    • 若为真,执行缩进后的 行动 代码块。
    • 执行完毕后,再次回到 while 处重新判断条件。
    • 只要条件持续为 True,循环就会不断执行;一旦条件变为 False,循环即刻停止。
  • 条件类型:条件可以是任何返回布尔值(TrueFalse)的表达式,例如比较运算或函数调用返回的结果。
  • 执行流程
    • 判断 while 后条件是否为 True
    • 若为真,执行下方缩进部分的内容,然后再次判断条件
    • 若仍为真,继续执行行动,直至条件为 False,推出循环。
  • 首次条件为假:若 while 后面的条件在第一次判断时就为 False,则行动一次也不会被执行。

while 循环初步应用示例:拍日落程序

Section titled “while 循环初步应用示例:拍日落程序”
  • 场景:当前天空亮度大约或等于500时,持续拍照片,直至亮度小于500
  • 优势:此例说明在条件合适结束未知的情况下,while循环比for循环更适合使用。
  • 三种遍历列表方式:前两段用 for range(len(list1)),第三段用 while
  • 结束值range 的结束值为 list1 长度。
  • i 值变化:i 依次赋值为 0, 1, 2, … 直到长度-1。
    • 长度-1 为最后一个元素索引。
    • 效果:打印列表所有元素。
  • 初始i = 0,0 < len(list1),进入循环打印第一个元素。
  • 循环i += 1 后为 1,再次判断 1 < len(list1),打印第二个元素。
  • 结束:i 达到列表长度,i < len(list1) 为假,推出循环。
  • for 优势:更简单直观,代码行数更少。
  • while 风险:更危险(如易死循环)。
  • 死循环成因:如果在 while 循环中忘记更新循环条件(例如忘记对计数变量 i 进行递增操作),条件将始终为 True,导致程序进入无限循环。
  • 示例对比
for i in range(...):
...
i = 0
while i < ...:
...
i = i + 1 # 若漏掉此行,i 永远为 0,导致死循环
  • for 循环:适用于有明确的循环对象或已知循环次数的场景,代码更简洁、更安全。
  • while 循环:适用于循环次数未知,需根据特定条件持续运行的场景(如等待用户输入特定字符)。
  • 功能需求:持续接收用户输入的数字并求平均值,直到用户输入 ‘q’ 结束输入。

  • 实现逻辑

    1. 使用 while 循环持续获取输入。
    2. 判断输入是否为 ‘q’,若是则跳出循环。
    3. 若非 ‘q’,将输入值累加并统计个数。
    4. 循环结束后计算并输出平均值。

用户输入平均值计算器的实现逻辑

Section titled “用户输入平均值计算器的实现逻辑”
  • 核心思路:利用 while 循环处理未知次数的用户输入,直到接收到终止信号 ‘q’。

  • 关键步骤

    1. 初始化变量:需要一个变量存储累加和,一个变量计数输入次数。

    2. 循环获取输入:使用 input() 函数持续获取用户输入。

    3. 条件判断:检查输入是否为 ‘q’。

      • 若是,退出循环。
      • 若否,将输入字符串转换为数字(如 float()),累加到总和并增加计数器。
    4. 计算输出:循环结束后,通过 总和 / 计数 计算平均值并输出。

  • 代码实现示例

sum_val = 0
count = 0
user_input = input("请输入数字(输入q结束):")
while user_input != 'q':
sum_val += float(user_input)
count += 1
user_input = input("请输入下一个数字(输入q结束):")
if count > 0:
print("平均值是:", sum_val / count)
  • 循环终止条件:在处理用户输入以 ‘q’ 结束的逻辑中,while 循环的条件应设置为 user_input != 'q'
    • 逻辑分析:只要输入的内容不等于 ‘q’,程序就继续执行循环体内的累加和计数操作;一旦输入为 ‘q’,条件变为 False,循环立即停止。
  • 初始化状态:在进入 while 循环前,必须定义 sum_val = 0count = 0,用于在循环过程中持续更新总和与输入次数。
  • 循环内输入更新
    • 循环体内的最后一行代码必须是再次调用 input() 函数,以获取用户的下一次输入。
    • 原因:若不更新 user_input 的值,循环将陷入死循环,因为条件 user_input != 'q' 永远为真。
  • 数据处理
    • 使用 float(user_input) 将输入的字符串转换为浮点数,以便进行数学运算。
    • 每次循环将转换后的数值累加到 sum_val,并将 count 加 1。
  • 条件检查:在循环结束后,应使用 if count > 0: 进行判断,防止在没有任何输入的情况下直接进行除法运算,避免程序因除以零(ZeroDivisionError)而崩溃。
  • 位置要求:累加操作必须放在while循环内、input()之前
    • 否则只执行一次,或首次输入未累加。
    • 若放input()后,用户输入’q’时会尝试float('q')导致程序崩溃。
  • 代码简写
total += num # 等价于 total = total + num
count += 1 # 等价于 count = count + 1
  • 计算与输出
    • 循环结束后,计算平均值:result = total / count
    • 使用 print 输出最终结果,并结合 str() 将浮点数转换为字符串进行拼接。
  • 问题场景:若用户在第一次输入时直接输入 ‘q’,则 count 保持为 0,直接执行 total / count 会引发 ZeroDivisionError
  • 解决方案:在计算平均值前增加条件判断:
if count > 0:
result = total / count
print("输入的数字平均值为:" + str(result))
  • 空输入风险:如果用户在第一次输入即选择终止(输入 ‘q’),程序必须能够优雅地退出,而不是尝试计算空集的平均值。
  • 数学原则:在 Python 及数学逻辑中,0 除以 0 是不允许的,因此必须显式地进行 count > 0 的检查以确保程序的健壮性。
  • 正常输入测试:输入多个数字后输入 q,正确输出平均值。
  • 立即退出测试:首次输入 q,程序优雅处理,无错误输出平均值。
  • 问题:群发祝福短信易显复印,降低诚恳感。
  • 解决方案:在内容插入收件人姓名,两处位置需动态替换。
  • 实现思路:用 for 循环 遍历联系人列表,将姓名拼接到短信模板正确位置。
  • 优势:代码自动化,编写长字符串不麻烦。
  • 痛点:使用字符串拼接(+)处理多处变量插入时,代码会被频繁打断,导致可读性差且容易出错。
  • 解决方案:使用 format() 方法,通过占位符 {} 优雅地插入变量。
message_content = """
律回春渐,新元肇启。
新岁甫至,福气东来。
{0}贺岁,欢乐祥瑞。
{0}敲门,五福临门。
{1}及家人拜年啦!
新春快乐,{0}年大吉!
""".format(year, name)
  • 原理
    • 大括号 {} 作为占位符,标记需要替换的位置。
    • 大括号内的数字(如 {0}, {1})对应 format() 函数括号内参数的索引位置。
    • 0 代表第一个参数 year1 代表第二个参数 name
  • 优势:保持了长字符串的完整结构,逻辑直观,避免了繁琐的引号和加号嵌套。

使用 format() 方法进行字符串格式化

Section titled “使用 format() 方法进行字符串格式化”
  • 解决痛点:使用 + 进行字符串拼接时,长字符串会被频繁打断,导致代码结构支离破碎、可读性差。
  • 核心原理:使用花括号 {} 作为占位符,配合 .format() 方法将变量按索引顺序填充。
message_content = """
律回春渐,新元肇启。
新岁甫至,福气东来。
{0}贺岁,欢乐祥瑞。
{0}敲门,五福临门。
{1}及家人拜年啦!
新春快乐,{0}年大吉!
""".format(year, name)
  • 索引对应关系
    • {0}:对应 format() 函数内的第一个参数(即 year)。
    • {1}:对应 format() 函数内的第二个参数(即 name)。
  • 优势:完整保留了长字符串的文本结构,逻辑直观,避免了繁琐的引号闭合和加号连接。
  • 灵活性提升:除了使用索引,还可以使用关键字参数,使代码更具可读性。
  • 实现方式:在 format() 中直接指定变量名,并在字符串中使用对应的名称作为占位符。
message_content = """
{current_year}贺岁,欢乐祥瑞。
{receiver_name}及家人拜年啦!
""".format(receiver_name=name, current_year=year)
  • 优势
    • 即使 yearname 在字符串中多次出现,也能通过关键字准确匹配。
    • 字符串中占位符的顺序不再受限于 format() 参数的顺序。
  • 语法特点:在字符串引号前添加字母 f,即可直接在花括号内引用变量。
  • 执行机制:花括号内的内容会被直接求值,并自动插入到字符串中。
message_content = f"""
新春快乐,{year}年大吉!
"""
  • 对比:相比 format() 方法,f-字符串写法更加简洁,直接将变量名嵌入字符串中,是现代 Python 开发中推荐的格式化方式。
  • 需求:在打印浮点数时,经常需要限制小数点后的位数,避免输出过长的小数。
  • 实现方式:在格式化占位符中添加 : .nf,其中 n 为保留的小数位数。
    • .2f 表示保留两位小数。
  • 使用&;.format()&;方法

print(“{0} 的当前绩点为: {1:.2f}”.format(name, gpa))

- **使用 f-string**
```python

直接在变量后的花括号内添加格式说明

Section titled “直接在变量后的花括号内添加格式说明”

print(f”{name} 的当前绩点为: {gpa:.2f}“)

  • 公式:$$Area = \frac{\theta}{360} \times \pi \times r^2$$
  • 实现逻辑
    • 定义变量 theta (角度) 和 r (半径)。
    • 使用 math.pi 获取圆周率。
    • 计算并输出结果。
import math
theta = 60
r = 10
area = (theta / 360) * math.pi * (r ** 2)
print(f"扇形面积为: {area:.2f}")
  • 重复场景:程序中多处需计算扇形面积,仅角度和半径不同,即产生一堆相似代码。
  • 示例:原型角 160°、半径 30,π取近似 3.14
  • 核心理念:不要重复自己,避免做代码复读机
  • 违反后果:重复代码增加维护难度,修改时易遗漏导致不一致。
  • 解决方案:使用函数封装重复逻辑。
  • 示例printsum 等自带函数,各负责特定任务。
  • 调用方式函数名(参数),如 print('hello')
  • 函数类比:制作负责特定任务的“机器”,定义一次,后续直接调用即可,无需重复编写代码。
  • 定义语法
def 函数名():

函数体代码(使用缩进划分范围)

Section titled “函数体代码(使用缩进划分范围)”
- `def`定义函数的关键字后跟函数名括号和冒号
- 缩进划分函数体代码范围 if/for 类似
- **实践步骤**将之前扇形面积计算代码整体移入函数定义中
- **执行规则**定义时函数体内代码**不执行**仅在调用时函数名括号才实际运行
  • 当前痛点:定义的 calculate_sector_1() 函数只能计算固定数值(角度160,半径30)。若要计算其他扇形,需重复定义 calculate_sector_2() 等,这导致了新的代码重复。
  • 解决方案:引入参数 (Parameters),使函数像“通用机器”一样,根据输入的不同原料(角度和半径)输出不同的结果。
  • 类比:将函数比作“包子机”。
    • 原始函数:只能做猪肉白皮包子。
    • 参数化函数:可以根据输入的肉馅和面粉颜色,自动生产对应口味和颜色的包子。
  • 参数化逻辑
    • 在定义函数时,在括号内设置变量名作为占位符。
    • 调用函数时,传入具体数值,函数内部逻辑会根据这些数值动态执行。
  • 动态化改造
def calculate_sector(central_angle, radius):

内部逻辑使用传入的参数进行计算

Section titled “内部逻辑使用传入的参数进行计算”
sector\_area = (central\_angle / 360) \* 3.14 \* (radius \*\* 2)
print(f"扇形面积为: {sector\_area}")

calculate_sector(160, 30)

calculate_sector(60, 15)

- **优势**极大地提高了代码的复用性只需编写一次逻辑即可处理无数种不同的输入情况
  • 移除编号:将函数名 calculate_sector_1 中的 1 全部删除,使其通用化,不限于第一个扇形。
  • 添加参数:在括号内放入 central_angle(圆心角角度)和 radius(半径)。
  • 删除冗余赋值:传入参数后,自动赋值给函数内变量,无需手动复值,可移除这两行代码。
  • 成果:获得计算并打印任意圆心角和半径的扇形面积函数。
  • 代码压缩:原本多行重复代码,封装函数后简化为几行调用。
  • 预告:此函数还可以做得更好,具体内容下视频分解。
  • 当前代码结构
def calculate_sector(central_angle, radius):
sector_area = (central_angle / 360) * 3.14 * radius ** 2
print(f'扇形面积为: {sector_area}')
  • 作用域 (Scope) 概念
    • 局部变量:在函数内部定义的变量(如 sector_area)属于局部变量,其生命周期仅限于函数内部。
    • 访问限制:函数外部无法直接访问函数内部的变量。即便函数执行后,外部也无法通过 sector_area 获取计算结果,因为该变量在函数运行结束后即被销毁。
  • 直观类比:函数内部就像一个封闭的院子,内部定义的变量是院里的树,院外的人(外部程序)无法直接看到或获取这些树。
  • 示例演示
def func():
a = 3
func()
print(a) # 报错:NameError,外部无法访问内部定义的 a
  • 结论:若需在函数外部使用计算结果,仅依靠 print 打印是不够的,必须引入后续机制(如返回值)来传递数据。
  • 生命周期:函数内部定义的变量(局部变量)仅在函数执行期间存在,函数运行结束后即被销毁。
  • 访问限制:函数外部无法直接引用函数内部的变量,尝试访问会导致 NameError
  • 示例
def func():
a = 3
func()
print(a) # 报错:NameError: name 'a' is not defined
  • 结论:若需在函数外部使用计算结果,仅依靠函数内的 print 是不够的,必须引入 return 机制将数据从函数“内部”传递到“外部”。
  • 功能:将函数内部计算出的值返回给调用者,使得函数执行后的结果可以被外部变量接收或进一步处理。

  • 语法:在函数最后一行使用 return 关键字后跟要返回的内容。

  • 执行机制

    1. 函数逐行执行内部语句。
    2. 函数调用完成后,return 将指定的值“带回”到调用位置。
    3. 返回值可以赋值给变量、直接打印或用于其他逻辑。
  • 代码示例

def func():
a = 3
return a
return_value = func() # 将函数返回的 3 赋值给变量
print(return_value) # 输出: 3
  • 默认行为:若函数中未写 return 语句,函数执行完毕后默认返回 None
  • 默认行为:如果函数体中没有显式编写 return 语句,函数执行完毕后会自动返回 None
  • 应用场景:像 print()list.append() 这类函数,其核心目的是执行某种操作(如打印到屏幕或修改列表),而非返回计算结果,因此它们在 Python 中本质上都是返回 None 的函数。
  • 调用建议:对于返回 None 的函数,通常直接调用即可,无需将其赋值给变量。
  • 计算与输出的分离
    • 仅使用 print 的函数类似于“只做包子不给食客”的机器,外部无法获取包子(计算结果)。
    • 引入 return 语句后,函数可以将计算结果“带回”给调用方,从而允许后续逻辑(如变量赋值、进一步计算)使用该结果。
  • 示例对比
    • 无&;return:调用函数仅触发内部打印,外部无法获取数据。
    • 有&;return:调用函数时,外部变量可以接收函数返回的值。

示例:通过 return 将结果传递给外部

Section titled “示例:通过 return 将结果传递给外部”

def calculate_sector(central_angle, radius):

sector\_area = (central\_angle / 360) \* 3.14 \* radius \*\* 2
return sector\_area

sector_area_1 = calculate_sector(160, 30)

- **类比理解**`return` 就像是函数执行后的打包带走功能没有它函数内部的计算成果在函数运行结束后即被销毁
  • 公式:BMI = 体重 / (身高²)

  • 函数要求

    1. 计算任意体重/身高的 BMI 值
    2. 执行过程中打印“您的 BMI 分类为 [类名]”(如偏瘦、正常、肥胖)
    3. 返回计算出的 BMI 值
  • 开始定义def 函数名():

  • 条件分支
    • BMI > 18.5 后,判断 BMI < 30 → 偏胖
    • BMI > 30 → 肥胖(用 else 概括)
  • 打印输出
print(f"您的bmi分类为{category}")
  • 返回结果
return BMI
  • 测试:函数编写完成,可进行测试验证。
  • 演示步骤:函数调用后打印BMI分类(如1.870对应的分类)。
  • 捕获返回值:创建变量复制函数的return值
  • 外部打印:打印变量查看BMI计算结果。
  • 效果:分类打印后,计算结果也成功输出。
result = calculate_BMI(...) # 接收return值
print(result) # 输出BMI数值
  • 内置函数:如 sum()print()len() 等,无需额外导入即可直接使用,适用于高频通用任务。
  • 标准库模块:当内置函数无法满足需求时(如计算中位数),可使用 Python 标准库中的模块。
  • 模块定义:本质上是一个包含函数和变量的 Python 程序,通过导入即可复用其中的功能。
  • 示例:statistics 模块
    • statistics 模块提供了统计相关函数,例如 median() 用于计算中位数。
    • 引入方式:import statistics
    • 调用方式:statistics.median(data_list)
  • DRY 原则 (Don’t Repeat Yourself):通过调用现成模块或自定义函数,避免重复编写逻辑,提高开发效率。
  • 代码简化:通过引入模块,复杂的逻辑(如手动排序及索引计算中位数)可以被压缩为极简的代码调用。
  • 内置函数:属于“高频家具”(如电视),随时可用。
  • 模块函数:属于“低频工具”(如烧烤炉),存放在“仓库”(模块)中,需要时再取出使用,避免占用全局命名空间,保持语言简洁。
  1. import 模块名:使用 模块名.函数名() 调用。
  2. from 模块名 import 函数名:直接使用 函数名() 调用。
  3. from 模块名 import *:导入模块内所有内容(不推荐,易产生命名冲突)。

模块导入方式的调用细节与注意事项

Section titled “模块导入方式的调用细节与注意事项”
  • 第一种(import 模块名)
    • 调用时必须使用模块名.函数名模块名.变量名
    • 示例:import statistics 后用 statistics.median()
  • 第二种(from 模块名 import 函数名)
    • 多个函数/变量用逗号分隔
    • 好处:直接调用函数名(),无需模块名前缀。
  • 第三种(from 模块名 import *)
    • 导入模块所有内容,直接调用。
    • 适合同一模块多个内容,但不推荐:易产生命名冲突
  • 命名冲突场景
    • 若同时从模块 A 和模块 B 使用 from ... import * 导入,且两者都包含同名函数(如 abc()),则后导入的模块会覆盖前者的定义。
    • 调用 abc() 时,程序无法明确指向,导致逻辑混乱或调用错误。
  • 最佳实践建议
    • 优先使用 import 模块名:通过 模块名.函数名() 明确调用来源,避免命名冲突。
    • 若必须使用 from ... import ...,应明确指定所需的特定函数名,而非使用 * 导入所有内容。
  • Python 官方文档 (docs.python.org)
    • 提供了标准库中所有内置模块的完整索引。
    • 模块涵盖了字符串处理、日期时间、文件操作、正则表达式、数学计算等多种功能。
    • 点击模块名称即可查看其包含的所有函数、变量及其详细用法说明。
  • 学习路径
    • 遇到编程需求时,先查询标准库文档,避免重复造轮子。
    • 通过文档示例(如 decimal 模块的精度处理)学习如何正确调用模块内的函数。
  • 查看模块源码
    • 在 PyCharm 中,可以通过快捷键直接跳转到函数定义,查看其内部实现逻辑。
    • Windows系统:按住 Ctrl 键并点击函数名。
    • macOS系统:按住 Command 键并点击函数名。
  • 代码复用与效率
    • 引入第三方库(非 Python 官方提供)能极大扩展程序功能。
    • 引入第三方库的语法与标准库一致,但前提是必须先通过包管理工具安装(类似从家具店将家具搬入自家仓库,再进行后续使用)。
  • 安装与引入的类比

    • 安装 (pip install):将家具从家具店搬入自家仓库(从互联网下载并配置到本地环境)。
    • 引入 (import):将仓库里的家具拿出来使用(在代码中加载模块)。
  • 第三方库的获取

    • 通过 PyPI.org 搜索并查看第三方库的介绍与用法。
  • 安装命令

    • 在终端 (Terminal) 执行:pip install 库名 (例如 pip install akshare)。
  • 使用流程

    1. 安装:pip install (只需执行一次)。
    2. 引入:import (在代码中调用)。
    3. 调用:使用库中的函数或方法。
  • 查找与调用
    • 在 PyPI 搜索特定功能的库(如 AkShare 财经数据接口)。
    • 安装后使用 import 引入,即可调用库内封装好的复杂函数(如 get_cffex_daily() 获取期货数据)。
  • 优势:极大扩展了 Python 的原生功能,通过复用他人构建的复杂逻辑,显著提高开发效率。

面向对象编程 (Object-Oriented Programming) 基础

Section titled “面向对象编程 (Object-Oriented Programming) 基础”

面向过程与面向对象的思维差异

Section titled “面向过程与面向对象的思维差异”
  • 面向过程 (Procedure-Oriented):关注执行步骤,将程序视为一系列指令的集合,直接聚焦于按顺序执行动作。
  • 面向对象 (Object-Oriented):关注对象 (Object) 本身,将任务分解到各个对象上,描述对象具备的属性(变量)和行为(函数),最后通过对象间的交互完成任务。
  • 类 (Class):对象的模板或蓝图,定义了该类对象共有的属性和方法。
  • 对象 (Object):类的具体实例。例如,人、洗衣机都是对象。
  • 属性 (Attribute):类中定义的变量,用于描述对象的状态。
  • 方法 (Method):类中定义的函数,用于描述对象的行为。
  • 逻辑封装:将相关属性和方法绑定在一起,不仅结构更清晰,还能更优雅地处理复杂逻辑。
  • 复用与扩展:通过定义类,可以轻松创建多个对象,并让它们执行各自的方法,避免了面向过程编程中重复编写步骤的繁琐。
  • 面向过程

放(“衣服”, “洗衣机”)

开机(“洗衣机”)

清洗(“洗衣机”)

烘干(“洗衣机”)

- **面向对象**
```python
class 洗衣机:
def 清洗(self, 需清洗物品): ...
def 烘干(self, 需烘干物品): ...

将逻辑封装在类中,通过对象调用

Section titled “将逻辑封装在类中,通过对象调用”

我的洗衣机 = 洗衣机()

我的洗衣机.清洗(“衣服”)

我的洗衣机.烘干(“衣服”)

  • 属性作为状态存储
    • 在类中,容量 可以直接作为对象的属性(self.容量)进行设置。
    • 优势:当对象被创建时即完成初始化,后续调用任何方法(如 清洗烘干)时,无需再将容量作为参数反复传入,直接通过 self 即可获取。
  • 逻辑清晰度
    • 将数据(属性)与行为(方法)绑定在对象内部,使得逻辑更加聚焦,减少了函数参数传递的冗余。
  • 封装 (Encapsulation)
    • 将内部实现细节隐藏,仅通过外部接口(方法)进行访问和操作。
    • 外部使用者只需知道方法名及其功能,无需关心内部如何实现。
  • 继承 (Inheritance)
    • 允许子类继承父类的属性和方法,实现代码复用。
  • 多态 (Polymorphism)
    • 允许不同对象对同一消息做出不同的响应。
  • 接口与实现分离
    • 类就像一个黑盒,__init__清洗烘干 等函数是其对外提供的接口。
    • 使用者通过调用这些接口来驱动对象,不需要了解内部具体的计算逻辑或实现细节。
  • 定义:允许创建具有层级关系的类,子类可以继承父类的属性和方法。
  • 现实类比:儿子继承父亲,父亲继承爷爷,体现了代码的从属与复用关系。
  • 解决的问题
    • 当多个类(如“小学生”和“大学生”)拥有大量共同属性(学号、年级)和方法(去学校)时,直接编写会导致大量重复代码。
    • 通过引入“学生”父类,将共有逻辑封装其中,子类直接继承,从而减少代码冗余。
  • 定义:同样的接口(方法名),针对不同的对象,表现出不同的具体行为。
  • 示例
    • 小学生执行 写作业() 可能是在做简单的算术题。
    • 大学生执行 写作业() 可能是在做复杂的积分运算。
  • 核心价值:调用者无需关心对象具体的类类型,只需通过统一的接口调用,程序会自动根据对象类型执行对应的逻辑。
  • 多态表现
    • 接口(方法名)统一,但对象类型不同,行为逻辑也不同。
    • 示例:写作业() 方法在“小学生”对象中调用表现为简单的加减法,在“大学生”对象中调用则表现为复杂的积分运算。
  • 核心价值
    • 调用者无需关心对象具体的类类型,只需通过统一接口调用,程序会自动根据对象类型执行对应的逻辑。
  • __init__&;方法
    • 类中定义的特殊方法,用于在创建对象时自动执行初始化逻辑。
    • 示例:self.name = "Lambton"name 属性绑定到该对象实例上。
  • self&;的作用
    • self 代表当前创建的对象实例。
    • 若赋值时不加 self.(如 name = "Lambton"),Python 会将其视为普通的局部变量,而非对象的属性,导致该值无法在对象外部通过 对象.属性名 访问。
  • 创建对象
    • 语法:变量名 = 类名()
    • 过程:调用类名时,会自动触发 __init__ 方法,并将该对象作为 self 传入。
    • 注意:self 参数无需手动传入,Python 会自动处理。
  • 获取属性
    • 使用点号操作符:对象.属性名(例如 print(cat1.name))。
  • 问题:硬编码属性(如 self.name = "Lambton")导致所有实例属性相同,缺乏灵活性。
  • 优化:在 __init__ 中添加参数,实现实例化时动态赋值。
class CuteCat:
def __init__(self, cat_name, cat_age, cat_color):
self.name = cat_name
self.age = cat_age
self.color = cat_color

cat1 = CuteCat(“Jojo”, 2, “橙色”)

- **原理**创建对象时`CuteCat("Jojo", ...)`),Python 自动将传入的参数传递给 `__init__` 方法从而实现每个对象拥有独特的属性值
  • 掌握属性封装与对象实例化,标志着从简单的指令执行转向面向对象编程,能够构建更具扩展性和逻辑性的程序结构。
  • 方法定义:方法本质上是定义在类内部的函数,用于描述对象能执行的行为(如“思考”或“叫唤”)。
  • 定义规则
    • 语法与普通函数定义类似,但在类内部定义。
    • 必须包含 self 参数,以便在方法内部访问该对象的属性。
  • 调用逻辑
    • 通过 对象名.方法名() 的方式调用。
    • Python 会自动将调用该方法的对象实例作为 self 传入,无需手动指定。
  • 函数:独立执行逻辑的工具。
  • 方法:封装在对象内部的逻辑,能够直接操作该对象的属性(如 self.name),实现数据与行为的绑定。
  • 方法定义规则
    • 方法本质上是类内部定义的函数,用于描述对象行为。
    • 必须包含 self 参数,以便方法内部能够访问该对象的属性(如 self.age)。
    • 定义语法与普通函数类似,但必须缩进在 class 内部。
  • self 的作用
    • self 代表对象自身,用于将方法与特定实例绑定,从而在方法内获取或修改该实例的属性。
    • 示例:在 speak(self) 方法中,通过 self.age 获取该猫咪对象的年龄。
  • 调用方式
    • 使用 对象名.方法名() 调用。
    • Python 会自动将调用该方法的对象实例作为 self 传入,无需手动指定。
  • 动态行为演示
    • 方法可以根据对象的属性值产生不同的输出,例如根据 self.age 的大小决定“喵”的重复次数。
    • 字符串乘法:"喵" * self.age,表示将字符串重复 age 次。
  • 代码示例
class CuteCat:
def __init__(self, cat_name, cat_age, cat_color):
self.name = cat_name
self.age = cat_age
self.color = cat_color
def speak(self):
print("" * self.age)
cat1 = CuteCat("Jojo", 1, "橙色")
cat1.speak()
  • 执行效果
    • cat1.age 为 1,输出
    • cat1.age 为 3,输出 喵喵喵
  • 核心价值:方法实现了对象数据(属性)与行为(逻辑)的统一,使得程序能够根据不同对象的状态展现出差异化的行为。
  • 方法接收参数
    • 类的方法不仅可以使用 self 访问属性,还可以像普通函数一样接收额外参数。
    • 示例:定义 think(self, content) 方法,将思考的内容作为参数传入,实现动态行为。
  • 调用逻辑
    • 调用时,self 参数由 Python 自动处理,仅需在括号内填入自定义参数。
    • 示例:cat1.think("现在去沙发还是去抓猫抓板")
  • 目标:创建一个 Student 类,包含姓名、学号及语文、数学、英语三科成绩。

  • 实现步骤

    1. 使用 class Student: 定义类。
    2. 使用 def __init__(self, ...): 初始化属性,将学生信息绑定到对象实例。
    3. 编写方法实现成绩设置与打印功能。
  • 类名规范:使用 class 关键字后跟大写开头的类名(如 Student)。
  • 构造方法:使用 __init__(注意双下划线)作为初始化方法,PyCharm 会自动补全 self 参数。
  • 属性绑定:在 __init__ 中通过 self.变量名 = 参数名 将传入的参数存储为对象的属性,保证每个实例拥有独立的数据。
  • 属性绑定逻辑
    • __init__ 方法中,使用 self.name = nameself.student_id = student_id 将传入的参数绑定到对象实例上。
    • 这种方式确保每个学生对象拥有独立的姓名和学号数据。
  • 成绩存储设计
    • 使用字典存储三科成绩,键为科目名称(字符串),值为成绩(初始设为 0)。
    • 字典定义:self.grades = {'语文': 0, '数学': 0, '英语': 0}
  • 初始化策略
    • 若所有学生对象的初始成绩均相同,则无需通过参数传入,直接在 __init__ 中定义即可,简化了实例化时的参数传递。
  • 对象实例化操作
    • 通过 类名(参数) 的方式创建实例,例如 chen = Student("小陈", "108618")
    • 实例化时,__init__ 方法被自动调用,传入的参数被赋值给对应的 self 属性。
  • 属性访问演示
    • 使用点号操作符直接读取实例属性:对象名.属性名
    • 示例:print(chen.name) 可获取该对象的姓名。
  • 对象方法调用
    • 同样使用点号操作符调用类中定义的方法,例如 对象名.方法名()
    • 验证:通过 print(zang.grades) 可以直接访问并打印该对象初始化后的成绩字典。

学生类方法定义与成绩修改逻辑

Section titled “学生类方法定义与成绩修改逻辑”
  • 定义&;set_grade&;方法
    • Student 类中定义 set_grade(self, course, grade) 方法,用于更新特定科目的成绩。

    • 方法内部逻辑:

      1. 检查 course(科目名称)是否存在于 self.grades 字典的键中。
      2. 若存在,则更新该科目的分数为传入的 grade 值。
      3. 若不存在,则忽略该操作,避免非法科目名称导致的错误。
def set_grade(self, course, grade):
if course in self.grades:
self.grades[course] = grade
  • 方法设计的关键点
    • 逻辑封装:将成绩更新的查验逻辑封装在方法内部,调用者只需提供科目和分数,无需关心字典是否存在该键。
    • 参数传递self 自动处理,coursegrade 作为外部传入的参数,实现了对象行为的动态化。
    • 防御性编程:通过 if course in self.grades 判断,确保了程序的健壮性,防止无效数据污染成绩字典。

学生类方法实现与成绩更新逻辑

Section titled “学生类方法实现与成绩更新逻辑”
  • 方法定义 (set_grade)
    • Student 类中定义方法以封装成绩更新逻辑,确保数据操作的规范性与安全性。
    • 语法:def set_grade(self, course, grade):
    • 逻辑:通过 if course in self.grades: 检查科目是否存在,仅在键存在时更新分数值,有效防止非法科目导致的错误。
  • 调用示例
    • 实例化对象后,通过 对象名.set_grade(科目, 分数) 调用方法。
    • 示例:zang.set_grade("数学", 95) 将小臧的数学成绩更新为 95。
  • 运行结果
    • 验证:通过 print(zang.grades) 输出字典,确认数学成绩已从初始值 0 变为 95。
  • 方法设计 (print_grades)
    • 定义 print_grades(self) 方法以统一输出学生信息及成绩。
    • 优势:无需额外参数,直接通过 self.grades 访问实例内部字典,实现数据的即时读取。
  • 代码实现
def print_grades(self):
print(self.name)
print(self.grades)
  • 逻辑优化
    • 在打印成绩前,可先打印 self.name 以增强输出的可读性,明确当前显示的是哪位学生的成绩。
  • print_grades 方法中,使用 for 循环遍历 self.grades 字典,可以更优雅地逐行展示科目与成绩。
  • 循环结构示例:
def print_grades(self):
print(f"学生{self.name} (学号: {self.student_id}) 的成绩为:")
for course in self.grades:
print(f"{course}: {self.grades[course]}分")
  • 逻辑说明
    • for course in self.grades:直接遍历字典的键(科目名称)。
    • self.grades[course]:通过键获取对应的值(分数)。
    • 这种方式比直接打印整个字典更具可读性,且符合面向对象中“对象展示自身状态”的封装原则。
  • 实践操作
    • 修改小陈的语文成绩数学成绩(使用 set_grade 方法)。
    • 调用 print_grades 方法(可能指 PrintGrate,打印所有成绩)。
  • 运行结果
    • 显示更新后的新成绩,全部正确展示。
  • 验证要点
    • 确认成绩修改生效,方法调用成功输出完整成绩列表。

人类与小猫的属性行为示例与继承引入

Section titled “人类与小猫的属性行为示例与继承引入”
  • 人类属性:名字、性别、两只眼睛没有尾巴
  • 人类行为呼吸阅读
  • 小猫属性:名字、性别、两只眼睛有尾巴
  • 小猫行为呼吸阿屎不能阅读抓沙发
  • 复读记代码:编写相似逻辑时(如人类和小猫的共同属性行为),发现大量重复。
  • DRY 原则Don’t Repeat Yourself):不要重复你自己,追求最小重复代码。
  • 继承特征:面向对象的重要机制,用于改写重复代码。
  • 应用:通过继承提取共同属性和行为,避免为每个对象重复编写相似逻辑。
  • 继承的实现语法
    • 在定义子类时,在类名后的括号中指定父类名称即可实现继承。
    • 语法示例:class Human(Mammal): 表示 Human 类继承自 Mammal 类。
  • 继承的执行逻辑
    • 属性继承:子类自动获得父类的属性初始化逻辑。若子类没有自己的 __init__ 方法,实例化子类时会自动调用父类的构造函数,确保子类对象具备父类定义的属性。
    • 方法继承:子类自动拥有父类定义的所有方法。调用子类对象的这些方法时,程序会直接执行父类中定义的逻辑。
  • 方法调用优先级
    • 当子类和父类拥有同名方法时,程序会优先调用子类自身的方法。
    • 只有当子类中不存在该方法时,程序才会向上查找并调用父类的方法。
  • 继承的实际应用
    • 将共有属性(如姓名、性别、眼睛数量)和共有行为(如呼吸、排泄)封装在父类(如 Mammal)中。
    • 子类(如 HumanCat)只需定义特有的属性或方法,从而大幅减少重复代码,遵循 DRY 原则。
  • 人类和小猫各自的 Hestel 属性值不同,不能只在父类中硬编码。
  • 若子类单独定义 __init__,实例化时优先调用子类构造函数,导致实例仅获得 Hestel 属性,丢失父类的 name、sex 等属性。
  • 解决方案:在子类 __init__全部重写 name、sex 等属性代码,但造成重复代码
  • super():返回当前类的父类
  • 在子类 __init__ 中调用 super().__init__(),自动执行父类构造函数。
  • 结果:子类实例同时获得父类属性(性别、眼睛数量等)和自身特有属性(Hestel)。
  • 成功移除重复的属性和方法代码行。
  • 实例化后,属性获取和方法调用与之前完全相同,无任何区别。
  • 判断标准:如果 A 和 B 可以说成 A 是 B 的一种,即可使用继承。
  • 员工分类:全职员工、兼职员工。
  • 共同属性姓名功耗
  • 共同方法:打印信息(输出姓名和功耗)。
  • 全职特有月薪属性,按月发工资。
  • 兼职特有按天发工资,有日期每月工作天数属性,计算月工资方法不同。
  • 父类Employee,封装姓名、功耗及打印信息方法。
  • 子类:全职(FullTimeEmployee)和兼职(PartTimeEmployee)继承Employee
  • 子类扩展:各自添加特有属性和月工资计算方法。
  • 先定义Employee父类。
  • 属性初始化:在 __init__ 中统一管理所有员工的共有属性 nameid
  • 方法封装:定义 print_info 方法,统一处理员工基本信息的打印逻辑,避免在子类中重复编写相同的输出代码。
class Employee:
def __init__(self, name, id):
self.name = name
self.id = id
def print_info(self):
print(f"员工名字: {self.name}, 工号: {self.id}")
  • 全职员工类&;FullTimeEmployee
    • 继承 Employee 父类,通过 super().__init__(name, id) 复用父类构造函数。
    • 扩展特有属性:monthly_salary(月薪)。
class FullTimeEmployee(Employee):
def __init__(self, name, id, monthly_salary):
super().__init__(name, id)
self.monthly_salary = monthly_salary
  • 计算公式:日薪 × 每月工作天数,比全职员工更复杂。
  • 创建张三兼职员工:传入姓名编号日薪月工作天数属性。
  • 创建小米4兼职员工:传入姓名编号日薪月工作天数属性。
  • 验证:打印两个员工的信息,确认属性设置正确。
  • 全职员工:直接返回 monthly_salary
  • 兼职员工:daily_salary × work_days
  • 不同子类拥有同名方法 calculate_monthly_pay(),但内部逻辑不同
  • 外部调用时自动执行对应类的计算逻辑,无需区分员工类型。
  • 调用各自的工资计算方法,打印每月工资。
  • 文件:文档、图片、音乐等统称。
  • 目录:文件夹,文件归属其中。
  • 手动:进入目录,点开文本编辑。
  • 代码:对文件进行读写操作。
  • Linux/MacOS 等类 Unix 系统
    • 树状结构,根节点为根目录,用 / 表示。
    • 所有文件目录存放在根目录下。
  • Windows 系统
    • 每个磁盘分区有独立根目录,用分区名 + 反斜杠表示(如 C:\)。
  • 根目录出发的路径。
  • 类 Unix 系统:以 / 开头,各级目录用 / 分割,最后为目标文件或目录。
  • Windows 系统:以分区名 + 反斜杠开头。
  • 定义:从一个参考位置(当前所在目录)出发的路径,而非从根目录出发。
  • 核心用途:表示当前位置与其他文件或目录的相对关系。
  • 关键符号
    • . (点):表示当前目录
    • .. (点点):表示上一级目录(父目录)。
  • 路径构造
    • 使用 ... 作为起点。
    • 目录间使用斜杠 / 或反斜杠 `` 分隔(取决于操作系统)。
    • 示例:../tmp/images/profile/1.jpg 表示从当前目录向上跳转一级,进入 tmp 目录下的 images 文件夹,再进入 profile 找到目标文件。
  • 省略规则:在当前目录下引用同级文件时,开头的 ./ 可以省略,直接使用文件名即可。
  • 识别特征:相对路径总是以 ... 开头(除非省略了 ./),区别于绝对路径的根目录开头方式。
  • 复制路径引用:在 IDE(如 PyCharm)中,右键点击文件并选择“复制路径/引用”,可以直接获取该文件的绝对路径或相对于项目根目录的相对路径。
  • 路径用途:获取文件位置后,即可在代码中进行后续的读写操作。
  • 数据处理自动化
    • 传统方式:手动复制数据到代码中,当数据文件(如不同日期的销售数据)变多时,需要反复修改代码,效率低下且容易出错。
    • 自动化方式:通过程序读取外部文件,仅需修改文件名即可处理不同数据,极大提升了代码的可维护性与复用性。
  • 基本语法open(file_path, mode, encoding)
    • file_path:文件的路径(支持相对路径或绝对路径)。
    • mode:操作模式,默认为 'r'(读取模式)。
    • encoding:编码方式,通常设置为 'utf-8' 以避免乱码。
  • 'r':读取模式(Read),只读文件内容。
  • 'w':写入模式(Write),只写文件内容。
  • 默认行为:若未指定模式,open() 默认以 'r' 模式打开。
  • FileNotFoundError:当程序尝试以读取模式打开一个不存在的文件路径时,会触发此错误,提示文件未找到。
  • 执行结果open() 函数若执行成功,会返回一个文件对象 (file object)
  • 后续流程:获取文件对象后,即可对其进行读取或写入操作,后续操作将基于此对象进行。
  • 调用 f.read() 可以一次性读取文件中的所有内容,并以字符串形式返回
  • 编程习惯:偏好 .txt纯文本格式,直接转换为字符串;避免 Word 等含字号、字体、颜色等复杂格式的文件,以防读取问题。
  • 程序记录文件读取位置(指针)
  • 首次 f.read() 后,指针移至文件末尾
  • 再次调用 f.read() 返回空字符串(无后续内容)。
  • 文件特别大时,避免 read(),因占用大量内存,甚至导致内存崩溃
  • 不想一次性读完,可给 read() 传参分批读取
f = open("./data.txt", "r", encoding="utf-8")
print(f.read()) # 读取全部内容并打印
print(f.read()) # 返回空字符串
  • 功能:每次只读取文件的一行内容,包含末尾的换行符 \n
  • 读取机制
    • 每次调用 f.readline() 读取当前行,并将读取位置(指针)移至下一行起始处。
    • 遇到文件末尾时,返回空字符串 ""
  • 应用场景:适合处理超大文件,避免一次性读取导致内存溢出。
f = open("./data.txt", "r", encoding="utf-8")
line = f.readline()
while line != "":
print(line)
line = f.readline()
  • 功能:一次性读取文件的所有内容,并将每一行作为一个元素存入一个列表中返回。
  • 特点
    • 方便对文件内容进行按行遍历或索引访问。
    • 同样会保留每行末尾的换行符 \n
print(f.readlines())

输出示例: [‘第一行内容\n’, ‘第二行内容\n’, ‘第三行内容’]

Section titled “输出示例: [‘第一行内容\n’, ‘第二行内容\n’, ‘第三行内容’]”
  • 文件读写完成后调用 f.close(),释放系统资源。
  • open() 函数成反义词对。
  • 语法
with open("./data.txt", "r", encoding="utf-8") as f:
- **优势**缩进代码块执行完毕后文件**自动关闭**无需手动调用 `close()`代码更简洁可靠
- 避免忘记关闭导致资源泄漏

实践:创建用于读取的文本文件

Section titled “实践:创建用于读取的文本文件”
  • 创建文件:在代码编辑器中右键新建文件(例如 data.txt),后缀 .txt 明确其为纯文本格式。
  • 路径与位置:文件在项目文件夹中的位置决定了读取时所需的路径(相对路径)。
  • 内容填充:在文件中输入内容(如诗歌),确保文件已保存。
  • 打开文件:使用 open() 函数,传入文件路径、模式 'r' 及编码 'utf-8'
  • 读取内容
    • 使用 f.read() 一次性读取全部内容。
    • 配合 with 语句实现自动关闭文件,无需显式调用 close()

with open(“./data.txt”, “r”, encoding=“utf-8”) as f:

content = f.read()
print(content)
  • 代码文件与目标文件同文件夹:使用 .(当前目录)+ 分隔符 + 文件名。
  • 类 Unix 系统(Linux/Mac):分隔符为 /,如 ./data.txt
  • Windows 系统:分隔符为 \,如 .\data.txt
  • mode=‘r’:读模式,可省略(open() 默认 ‘r’)。
  • encoding=‘utf-8’推荐显式指定,避免系统默认编码导致乱码。
  • open() 成功执行返回文件对象,用于后续读写操作。
  • 三行代码(open/read/print/close)运行后,成功打印文件全部内容,与原文件完全一致
  • 替换步骤:删除手动 f.close() 行,使用 with open("./data.txt", "r", encoding="utf-8") as f:
  • 缩进要求:所有读文件操作(如 content = f.read(); print(content))置于 with 代码块缩进下
  • 优势:自动关闭文件,避免忘记 close()
  • readline() 逐行读取,与 read()(一次性读全部)结果相同但更适合大文件。
print(f.readline())
print(f.readline()) # 读取前两行内容
  • with 语句块内所有文件操作必须缩进。
  • 无缩进时文件已自动关闭,调用 f.readline() 会报错。
  • readline() 读取行尾 \n
  • print() 默认后加换行。
  • 两者结合产生多一空行。
  • 功能:一次性读取文件所有内容,并将其作为字符串列表返回,列表中的每个元素对应文件的一行。
  • 特点
    • 自动包含行尾的换行符 \n
    • 适合与 for 循环结合使用,遍历处理每一行。
  • 代码示例
with open("data.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
print(line)
  • 优势:相比 readline() 需要手动循环,readlines() 直接将文件内容转化为列表,代码结构更简洁,避免了手动维护循环计数器。
  • 覆盖写入:使用 'w' 模式打开文件时,若文件已存在,原有内容会被清空;若文件不存在,则会创建新文件
  • 与读模式的相似性:同样需要使用 with open(...) as f: 结构,操作完成后自动关闭文件。
  • write() 方法:将字符串直接写入文件。
    • 注意write() 不会自动添加换行符,若需换行需手动拼接 `

`。

  • 代码示例
with open("./output.txt", "w", encoding="utf-8") as f:
f.write("姓名:张伟\n")
f.write("平均分:85")
模式 描述 存在时行为 不存在时行为
‘r’ 只读 读取内容 报错 (FileNotFoundError)
‘w’ 只写 清空原内容 创建新文件
  • 将程序计算结果存入文件,避免重复运行程序计算,同时方便将结果分享给他人。
  • write() 方法写入字符串,连续调用直接拼接,无自动换行。
  • 需手动添加 \n 实现换行。
with open("./data.txt", "w", encoding="utf-8") as f:
f.write("Hello!")
f.write("Yoooo") # 结果: Hello!Yoooo
with open("./data.txt", "w", encoding="utf-8") as f:
f.write("Hello!\n")
f.write("Yoooo") # 结果: Hello!\nYoooo (两行)
  • 功能:在文件末尾添加内容,而不是像 ‘w’ 模式那样清空原有内容。
  • 特性
    • 若文件不存在,同样会创建新文件。
    • 适合在已有数据的基础上持续记录,如日志或追加记录。
  • 代码示例
with open("./data.txt", "a", encoding="utf-8") as f:
f.write("Hello!\n")
f.write("Yoooo")
  • 读取限制:在 ‘w’ 或 ‘a’ 模式下,直接调用 read() 会报错 UnsupportedOperation,因为这些模式仅设计用于写入。
  • 同时读写需求:若需在同一文件对象上同时进行读取和写入,应使用 'r+' 模式。
  • ‘r+’ 模式优势
    • 允许在打开文件后调用 read() 读取内容。
    • 允许调用 write() 在文件末尾追加内容,而不会清空原有数据。
    • 避免了多次 open() 带来的繁琐操作。

文件写入实践任务:写入 PoM表TX.txt

Section titled “文件写入实践任务:写入 PoM表TX.txt”
  • 创建新文件 PoM表TX.txt
  • 使用 ‘w’ 模式:文件不存在时自动创建。
  • 写入三句词
  • 相对路径:同文件夹下用 "PoM表TX.txt""./PoM表TX.txt"./ 可省略)。
with open("PoM表TX.txt", "w", encoding="utf-8") as f:
f.write("第一句\\n")
f.write("第二句\\n")
f.write("第三句")
- 路径相对路径代码文件同文件夹
- 编码传入 `encoding="utf-8"` 确保兼容中文
  • write() 方法不会自动在写入的字符串后添加换行符。
  • 若要实现多行效果,必须在字符串末尾手动添加 \n
with open("poem.txt", "w", encoding="utf-8") as f:
f.write("第一句\n")
f.write("第二句\n")
f.write("第三句")
  • 分次写入:通过多次调用 write() 逐步写入,需注意手动添加换行符以保持格式正确。
  • 一次性写入:可以将所有内容拼接成一个长字符串,通过一次 write() 调用完成写入,代码更简洁。

with open(“poem.txt”, “w”, encoding=“utf-8”) as f:

f.write("第一句\\n第二句\\n第三句")
  • 场景需求:在已有的 poem.txt 文件末尾添加两行新内容,而不清空原有数据。
  • 模式选择:使用 'a' (Append) 模式,避免 'w' 模式导致的覆盖清空。
with open("poem.txt", "a", encoding="utf-8") as f:
f.write("起舞弄清影\n")
f.write("何似在人间。\n")
  • 关键细节
    • 换行符管理:在追加内容时,必须在每行字符串末尾添加 \n。若不添加,新写入的内容会直接连接在原文件末尾的字符之后。
    • 防止粘连:若原文件末尾本身没有换行符,追加内容时需在开头额外添加一个 \n,否则新内容会与原文件最后一行“粘”在一起。
  • 代码逻辑错误:即便代码本身语法正确,也可能因外部因素导致程序运行中断(崩溃)。
  • 输入数据不合理:例如用户输入了程序无法处理的数据类型或格式,会导致程序在运行过程中产生报错。
  • 资源访问失败:如尝试打开不存在的文件,程序会抛出异常并停止执行后续代码。
  • 程序中断机制:当程序在某一行产生报错时,后续所有代码将不会被执行。
  • 预防性编程:学习在程序“炸掉”之前进行预判,捕获异常,并以预期的逻辑处理错误,从而避免程序完全停摆。
  • IndexError (索引错误):当尝试访问列表长度范围之外的索引时触发。
  • ZeroDivisionError (除零错误):当尝试用数字除以 0 时触发。
  • FileNotFoundError (找不到文件错误):当尝试打开一个不存在的文件时触发。
  • TypeError (类型错误):当操作数类型不匹配时触发,例如尝试将两个字符串进行乘法运算。
  • 程序某行抛异常时,后续代码停止执行。
  • 通过预防性编程预判错误,优雅处理避免崩溃。
  • try-except 语句
    • try: 后缩进,放可能报错代码。
    • except 错误名: 后捕捉特定异常。
  • 示例:float() 转换无法转为数字的字符串,报 ValueError
  • 用户输入非数字(如诗句),float() 转换失败,程序直接崩溃。
  • 无机会提示用户,需提前捕捉异常保持程序运行。
  • 核心逻辑:将可能引发错误的代码放入 try 代码块中,并在 except 块中定义错误发生后的补救逻辑。
  • 程序防崩溃:通过捕获异常,程序在遇到错误时不会直接停止执行,而是转而执行 except 中的代码。
  • 代码示例
try:
user_weight = float(input("请输入体重(kg): "))
user_height = float(input("请输入身高(m): "))
user_BMI = user_weight / user_height ** 2
except ValueError:
print("输入不合规数字,请重新运行程序并输入正确数字。")
  • 针对性捕获:可以为不同的异常类型编写多个 except 块,分别处理不同错误(如 ValueErrorZeroDivisionError)。
  • 通用异常捕获:若不确定错误类型,可使用不带错误名的 except:,它会捕获所有异常类型,防止程序崩溃。
except ZeroDivisionError:
print("身高不能为零,请重新运行程序。")
except:
print("发生了未知错误,请重新运行程序。")
  • 注意事项try-except 语句在执行时是从上往下进行异常匹配的。

异常处理中的 else 与 finally 语句

Section titled “异常处理中的 else 与 finally 语句”
  • else 语句
    • 放置在 except 块之后。
    • 执行逻辑:仅当 try 块中的代码没有发生任何异常时才会执行。
    • 应用场景:适合放置在成功读取数据后需要执行的后续逻辑,例如打印计算结果。
  • finally 语句
    • 放置在 exceptelse 块之后。
    • 执行逻辑无论是否发生异常,也无论异常是否被成功捕获,finally 块中的代码总是会被执行。
    • 应用场景:用于必须执行的清理工作(如关闭资源、打印结束语)。
try:
user\_weight = float(input("请输入体重(kg): "))
user\_height = float(input("请输入身高(m): "))
user\_BMI = user\_weight / user\_height \*\* 2

except ValueError:

print("输入不合规数字,请重新运行程序并输入正确数字。")

except ZeroDivisionError:

print("身高不能为零,请重新运行程序。")

except:

print("发生了未知错误,请重新运行程序。")

else:

print("您的BMI值为: " + str(user\_BMI))

finally:

print("程序结束运行。")
  • 代码测试确保代码无误。
  • 目标:在用户发现前快速发现并改正bug
  • 1947年,格蕾丝·赫柏发现计算机故障原因是蛾子(英文bug)进入继电器导致硬件问题。
  • Bug 在计算机领域表示程序错误
  • 验证新代码是否正确。
  • 确认修改老代码后,不该受影响的部分仍按预期执行(避免牵一发而动全身)。
  • Assert:中文断言
  • 后跟布尔表达式(求值为 True 或 False)。
  • True:无事发生,继续执行后续代码。
  • False:抛出 AssertionError(断言错误),提醒“这里不符合预期”。
assert 1 == 1 # True,继续运行
assert 1 == 2 # False,抛 AssertionError,程序终止
  • 一旦抛 AssertionError,程序立即终止,后续代码不会执行。
  • Assert 的局限性:由于 assert 抛出错误后会立即终止程序,不适合大规模、多用例的测试。
  • 测试库的优势
    • 支持一次性运行多个测试用例。
    • 能直观展示哪些用例通过,哪些未通过。
  • 定义:Python 自带的常用单元测试库,无需额外安装。
  • 核心概念
    • 单元测试:对软件中最小可测试单元(如函数)进行验证,确保其表现符合预期。
    • 导入方式:虽然是内置库,但仍需使用 import unittest 引入。
  • 工程规范
    • 代码分离:将测试代码与实现代码放在独立的 .py 文件中,避免功能代码与测试逻辑混杂。
    • 结构清晰:这种分离方式有助于更清晰地划分实现逻辑与验证逻辑。
  • 代码导入与结构
    • 在测试文件中,需通过 from 文件名 import 函数名/类名 引入待测代码。
    • 必须 import unittest 以使用测试框架。
  • 创建测试类
    • 定义一个继承自 unittest.TestCase 的类,类名建议以 Test 开头。
    • 继承该类后,即可使用其提供的各种断言和测试功能。
  • 编写测试方法
    • 每一个测试用例都是类中的一个方法。
    • 方法名必须以 test_ 开头,unittest 会自动识别并运行这些方法。
import unittest
from my_calculator import my_adder
class TestMyAdder(unittest.TestCase):
def test_positive_with_positive(self):
pass
def test\_negative\_with\_positive(self):
pass
  • 测试逻辑实现
    • 继承 unittest.TestCase 后,可以直接使用 self.assertEqual(a, b) 方法进行断言。
    • assertEqual 的逻辑:若第一个参数与第二个参数相等,测试通过;否则测试不通过,但程序不会像 assert 那样直接终止,而是会记录失败信息并继续执行后续测试用例。
  • 运行测试用例
    • 在编辑器终端输入以下命令运行测试:
Terminal window
python -m unittest
- **自动发现机制**:该命令会自动搜索当前目录下所有继承自 `unittest.TestCase` 的子类,并运行其中所有以 `test_` 开头的方法。
- 运行结果会显示测试用例的执行数量(如 `Ran 2 tests`)及执行耗时,直观反馈代码质量。
  • 执行反馈:运行 python -m unittest 后,系统会显示运行的测试用例总数及耗时。
  • 点号表示法:输出中的点号(.)代表测试用例通过。
  • 失败提示:若测试未通过,会显示 FAIL 并提供详细的错误回溯(Traceback),明确指出是哪个文件、哪个方法导致了失败以及失败原因。
  • 一体化报告unittest 提供了一体化的测试结果,包含所有测试用例的执行状态和详细的错误信息,极大地提高了单元测试的效率。
  • 自动化发现:无需手动调用每个测试函数,框架会自动识别并运行所有 test_ 开头的方法。
  • 高效调试:通过详细的失败报告,开发者能快速定位问题所在,避免了手动运行代码检查的繁琐过程。

除了 assertEqualunittest.TestCase 还提供了多种断言方法以应对不同测试场景:

方法 类似于 适用场景
assertTrue(A) assert A is True 验证条件是否为真
assertIn(A, B) assert A in B 验证元素是否存在于序列中
assertNotEqual(A, B) assert A != B 验证两个值不相等
assertFalse(A) assert A is False 验证条件是否为假
assertNotIn(A, B) assert A not in B 验证元素不在序列中
  • 万能替代:本质上 assertTrue 可以替代所有其他断言方法(例如 assertTrue(2 not in [1, 2]) 等同于 assertNotIn(2, [1, 2]))。
  • 推荐做法:应优先使用具有针对性的断言方法(如 assertNotIn)。
  • 原因:当测试未通过时,针对性方法会给出更详细的失败原因,而 assertTrue 只会报错“False is not true”,难以快速定位问题。
  • 解决重复代码:在测试类中,若多个测试方法都需要先实例化同一个对象,可以使用 setUp(self) 方法。
  • 执行机制setUp 方法会在每一个以 test_ 开头的方法运行前被自动调用一次,确保每个测试用例都在一个干净、独立的初始状态下运行。
  • 属性共享:在 setUp 中通过 self.变量名 = 对象 将对象设为类的属性,后续的测试方法即可直接通过 self.变量名 调用该对象,避免了在每个方法中重复编写实例化代码。
class TestSentence(unittest.TestCase):
def setUp(self):

每个测试方法运行前都会执行此初始化

Section titled “每个测试方法运行前都会执行此初始化”
self.sentence = Sentence("hello world!")
def test\_str\_count(self):

直接使用 setUp 中创建的 self.sentence

Section titled “直接使用 setUp 中创建的 self.sentence”
self.assertEqual(self.sentence.str\_count(), 12)
  • 新建文件:test_shortlist.py(以 test_ 开头,便于 unittest 识别)。
  • 导入模块:
import unittest
from 文件名 import ShoppingList
  • 创建继承 unittest.TestCase 的类。
  • get_item_count():返回购物清单商品件数。
  • get_total_price():返回购物清单商品总额。
  • 方法名以 test_ 开头。
  • 示例清单:纸巾8元、帽子30元、拖鞋15元(共3件商品)。
  • 验证 getItemCount() 返回3。
def test_getItemCount(self):
self.assertEqual(self.shopping_list.getItemCount(), 3)
  • 测试目标:验证 get_total_price() 方法计算总价的准确性。
  • 测试逻辑:使用包含“纸巾: 8, 帽子: 30, 拖鞋: 15”的购物清单,预期总价应为 53。
def test_get_total_price(self):
self.assertEqual(self.shopping_list.get_total_price(), 53)
  • 问题:在多个测试方法中重复实例化 ShoppingList 对象会导致代码冗余。
  • 解决方案:使用 setUp() 方法在每个测试用例运行前自动初始化对象。
  • 实现细节
    • setUp 中通过 self.shopping_list = ShoppingList(...) 将对象设为类属性。
    • 后续测试方法直接通过 self.shopping_list 调用,无需重复创建。
def setUp(self):
self.shopping_list = ShoppingList({'纸巾': 8, '帽子': 30, '拖鞋': 15})
  • 终端命令:python -m unittest
  • 运行2个测试用例,结果显示两个点号.),表示全部通过
  • 故意修改数字为错值,重新运行命令。
  • 结果显示Ffailure),表示有测试失败。
  • 框架自动展示失败的具体测试方法
  • 提供详细Traceback信息,包括出错文件、行号及原因(如 AssertionError: 53 != 55)。
  • 失败指标:当测试未通过时,终端显示 F (Failure) 而非 . (Success)。
  • 错误定位:框架会自动打印出失败的测试方法名称,帮助快速锁定问题区域。
  • 详细追踪 (Traceback)
    • 自动生成错误报告,明确指出出错的文件名、代码行号。
    • 提供具体的断言失败原因,例如 AssertionError: 53 != 55,直接对比预期值与实际值,极大简化了排查逻辑错误的过程。
  • 原有的函数同时负责“计算”与“打印”,导致扩展功能(如增加三次方计算)时代码结构变得冗余且难以维护。
  • 核心思路:将计算逻辑从打印函数中剥离,作为参数传入,实现逻辑解耦。
  • 可以将计算函数(如求平方、立方或加法)直接作为参数传递给打印函数,使打印函数专注于输出格式,而无需关心具体的计算实现。
def calculate_square(num):
return num * num
def calculate_cube(num):
return num * num * num
def calculate_plus_10(num):
return num + 10
def calculate_and_print(num, calculator):
result = calculator(num)
print(f"数字参数 | {num} | 计算结果 | {result}")

calculate_and_print(3, calculate_square)

calculate_and_print(3, calculate_cube)

- **优势**打印函数不再需要使用 `if-elif` 判断逻辑无论传入何种计算规则打印函数均能通用符合 DRY 原则
  • 定义:将函数作为参数传递给另一个函数的函数,称为高阶函数。
  • 核心优势:通过将计算逻辑(如平方、立方、加法)从打印函数中剥离,实现了逻辑解耦,使代码更符合 DRY (Don’t Repeat Yourself) 原则。
  • 调用规范
    • 传入函数本身时,不要带括号和参数(例如 calculate_square),否则会直接执行该函数并将结果作为参数传入,而非函数本身。
    • 只有在函数内部需要执行时,才通过 calculator(num) 形式进行调用。
  • 当需要增加新的计算功能(如乘以5)时,无需修改现有的 calculate_and_print 函数,只需定义新的计算函数并作为参数传入即可。
  • 甚至可以将格式化函数(如 print_with_vertical_bar)也作为参数传入,实现打印样式的动态切换。
def calculate_times_5(num):
return num * 5

calculate_and_print(7, calculate_times_5)

calculate_and_print(7, calculate_times_5, print_with_vertical_bar)

  • 定义与场景:当一个函数仅被调用一次,且逻辑非常简单时,专门定义一个具名函数显得过于正式且冗余。
  • 核心特点
    • 无需名称:直接在需要的地方定义,即用即弃。
    • 一次性:像一次性用品一样,使用后即销毁。
  • 应用示例:在将函数作为参数传递给高阶函数时,若该函数仅在该处使用,使用 lambda 可大幅简化代码结构。

def calculate_times_5(num):

return num \* 5

calculate_and_print(7, calculate_times_5)

calculate_and_print(7, lambda num: num * 5)

  • 关键字:使用 lambda 关键字定义。
  • 结构lambda 参数: 返回值
  • 限制:仅支持单行表达式,自动返回表达式的结果,无需显式使用 return 语句。
  • 定义方式:使用 lambda 关键字,后跟参数,冒号分隔,最后是返回的表达式。
  • 语法结构lambda 参数: 表达式
  • 核心优势
    • 无需命名:即用即弃,非常适合作为高阶函数的参数。
    • 简洁性:无需编写 defreturn,单行即可完成逻辑定义。
    • 代码优化:将原本需要额外定义具名函数的场景压缩至一行,提升代码可读性。

使用 lambda 代替具名函数传入高阶函数

Section titled “使用 lambda 代替具名函数传入高阶函数”

calculate_and_print(7, lambda num: num * 5)

特性 具名函数 (def) 匿名函数 (lambda)
名称 必须有
定义方式 完整函数体 单行表达式
返回值 需显式 return 自动返回表达式结果
适用场景 复杂逻辑、多次调用 简单逻辑、一次性调用
  • 若需多个参数,在 lambda 关键字后用逗号分隔参数名即可。

lambda num1, num2: num1 + num2

- **总结**匿名函数本质上是没有名字占用行数更少的函数”,特别适合只需要一次性执行的简单逻辑
  • 调用方式:与普通函数相同,使用括号传入参数;前面的匿名函数需加括号表示整体。
(lambda num1, num2: num1 + num2)(2, 3)
  • 单表达式限制:冒号后仅支持一个表达式,无法包含多语句、循环或递归。
  • 适用场景:仅限简单逻辑;复杂多步骤逻辑使用具名函数(def),以确保灵活性和可读性。
  • 可读性问题:复杂逻辑用 lambda 会导致代码难懂,应优先 def 定义普通函数。
  • 定义:基于网页的交互式计算环境,广泛用于数据分析、数据科学及机器学习领域。
  • 核心能力
    • 编写并运行 Python 代码。
    • 查看代码输出与可视化数据。
    • 编写并分享包含代码、公式与说明文档的报告。

为什么数据科学家偏爱 Jupyter Notebook

Section titled “为什么数据科学家偏爱 Jupyter Notebook”
  • 单元格执行机制
    • 优势:支持按单元格(Cell)独立运行代码,无需每次从头运行整个脚本。
    • 场景:在处理大规模数据或耗时操作时,避免了重复读取数据或执行前置步骤的等待时间,极大提升了调试与分析效率。
    • 对比:传统代码编辑器(如 PyCharm/VSCode)通常执行整个 .py 文件,而 Jupyter 允许在修改分析公式后,仅重新运行特定单元格查看效果。
  • 开发流程优化
    • 支持将不同的数据处理步骤(如读取、清洗、计算、保存)拆分到不同单元格中,实现灵活的交互式开发。
  • 代码片段执行:支持仅运行特定的单元格(Cell),无需重新执行整个脚本。这在处理大规模数据集或耗时的数据预处理步骤时,能显著节省重复等待的时间。
  • 开发流优化:通过将读取、清洗、计算、可视化等步骤拆分为独立的单元格,开发者可以针对特定逻辑进行反复调试,极大提升了数据分析的效率。
  • 富文本支持:支持 Markdown 标记语言,可以编写带标题、层级、列表的说明文档,使代码逻辑与分析思路一目了然。
  • 数学公式渲染:内置对 LaTeX 的支持,能够清晰地展示复杂的数学公式,便于学术交流与报告撰写。
  • 结果持久化:将代码、输出结果(包括图表)和说明文档整合在同一份文件中,分享时对方无需重新运行代码即可查看完整的分析过程及结论。
  • Windows:打开 CMD,输入 pip install notebook 回车安装。
  • macOS:打开 Terminal,输入 pip3 install notebook 回车安装。
  • 启动:输入 jupyter notebook,浏览器自动打开界面即成功。
  • 关闭:关闭浏览器不够,需在命令行 Ctrl+C 中止后台服务器。
  • 后台服务器中止:仅关闭浏览器窗口无法停止 Jupyter Notebook 服务,必须在运行它的终端(Terminal/CMD)中进行操作。

  • 关闭步骤

    1. 在运行 Jupyter 的终端窗口中,按下 Ctrl + C 组合键。
    2. 系统会提示确认是否关闭(Shutdown this notebook server (y/n)?)。
    3. 输入 y 并回车,即可安全终止后台服务。
  • 主界面功能
    • 启动后显示的面板即为当前根目录,展示所有文件与文件夹。
    • 支持通过点击文件夹名称进入子目录,实现层级管理。
  • 创建新 Notebook
    • 在目标文件夹内,点击右上角的 New 按钮,选择 Python 3 即可创建一个新的 .ipynb 文件。
    • 系统会自动在浏览器打开编辑界面,并在对应目录下生成该文件。
  • 文件重命名
    • 在编辑界面,直接点击顶部的标题(默认为 Untitled),在弹出的对话框中输入新名称即可修改。
  • 界面布局
    • 标题栏下方依次为:菜单栏、工具栏、单元格(Cell)。
    • 工具栏:集成了菜单栏中最常用的功能,便于快速操作。
  • 单元格功能:主要用于编写 Python 代码和 Markdown 文字说明。
  • 编辑模式与命令模式切换
    • 编辑模式:点击单元格内部,外框变为绿色,可输入代码或文字。
    • 命令模式:按 Esc 键或点击单元格外部,外框变为蓝色,此时可对单元格进行操作(如删除、移动)。
  • 保存:点击磁盘图标保存当前 Notebook 内容。
  • 新建单元格:点击 + 号在当前单元格下方插入新单元格。
  • 剪切/复制/粘贴:工具栏提供快捷按钮,支持对选中的单元格进行编辑。
  • 多选操作:按住 Shift 键可选中多个单元格,实现批量操作。
  • 移动单元格:使用上下箭头按钮可调整单元格的排列顺序。
  • 执行状态标识
    • 运行中:单元格左侧显示 [*],表示代码正在执行。
    • 执行完毕[*] 会变为数字(如 [1]),数字代表代码执行的顺序。
  • 灵活执行顺序
    • Jupyter 允许以任意顺序运行单元格,无需遵循从上到下的线性逻辑。
    • 即使是已执行过的单元格,也可以随时返回重新运行。
  • 执行顺序对变量的影响
    • 单元格左侧的数字记录了代码的实际执行先后顺序,这在调试时至关重要。
    • 风险提示:如果先运行了后面的单元格(如第3格),再运行前面的单元格(如第1格),程序状态会基于当前的执行顺序更新。若后续代码依赖之前的变量,执行顺序的混乱可能导致数据未及时更新或逻辑错误。
  • 交互模式执行:代码无需显式调用 print() 即可直接显示执行结果,适合快速查看变量值或中间计算结果。
  • 单单元格多语句限制:当一个单元格内包含多条输出语句时,系统仅展示最后一行代码的输出结果。
  • 强制打印:若需在单单元格中展示多项输出,必须显式使用 print() 函数。
  • 程序中断:使用工具栏的“停止”(Interrupt)按钮可强制中止正在运行的单元格代码,适用于陷入死循环或执行时间过长的任务。
  • 环境重置
    • 重启内核 (Restart):通过重启按钮清除所有已定义的变量和内存状态,相当于重置了当前的 Python 环境。
    • 应用场景:当变量定义混乱或需要从零开始重新验证逻辑时,重启内核是确保代码运行环境纯净的有效手段。

Jupyter Notebook 单元格运行与 Markdown 支持

Section titled “Jupyter Notebook 单元格运行与 Markdown 支持”
  • 多次运行:运行第二格输出对应值,变量在单元格间持久存在。
  • 重启后:直接运行第二格提示变量不存在,单元格数字重置为 [1] 表示已重启。
  • 按钮位置:单元格数字旁边。
  • 实用性:从上到下完整执行所有代码,检查单元格顺序问题
  • 右边下拉框:切换代码Markdown单元格。
  • Markdown 用途:添加文字样式,不限于代码。
  • 一级标题# 标题(井号空格),运行后自动加大加粗。
  • 二级标题## 标题,双击单元格进入编辑模式修改并运行渲染。
  • 标题层级
    • Markdown 单元格支持通过井号 (#) 定义标题层级,井号数量对应标题级别(如 # 为一级,## 为二级)。
    • 标题需在井号后添加空格才能正确渲染。
  • 样式编辑
    • 渲染后的 Markdown 单元格可通过双击进入编辑模式,修改后再次运行即可更新样式。
  • 文本格式化
    • Markdown 提供了丰富的文本修饰功能,除了标题外,还支持加粗、斜体、列表、链接等多种排版方式,便于在代码旁编写说明文档。
  • 公式渲染
    • 支持 LaTeX 语法,可在 Markdown 单元格中通过特定的数学表达式语法(如使用美元符号包裹)渲染复杂的数学公式。
    • 示例:$f(x) = x^2 + y^2 = 1$ 可在单元格内渲染为数学公式。
  • 快捷键配置面板
    • 工具栏最右侧的键盘图标用于打开“快捷键配置”(Keyboard Shortcuts)面板。
    • 通过此面板可以查看并自定义 Jupyter Notebook 的所有快捷操作,熟练使用快捷键能显著提升交互效率。
  • 常用快捷操作(命令模式下)
    • A (Above):在当前单元格上方插入一个新单元格。
    • B (Below):在当前单元格下方插入一个新单元格。
    • 掌握这些快捷键可避免频繁使用鼠标点击工具栏,保持编程流的连贯性。

Jupyter Notebook 命令模式快捷键实践

Section titled “Jupyter Notebook 命令模式快捷键实践”
  • 双D删除:在命令模式下,连按两次 D 键删除当前选中的单元格。
  • 模式要求:必须在命令模式(外框蓝色)使用,若在编辑模式(外框绿色)则视为输入文字。
  • Shift + Enter:运行当前单元格(适用于代码或Markdown),效果等同工具栏运行按钮。
  • 优势:自动跳转下一单元格,节省鼠标操作时间。
  • 可编辑分享:直接发送 .ipynb 文件,对方可自行编辑运行。
  • 只读分享:点击 File > Download as > HTML,生成网页格式报告,便于阅读无需运行环境。
  • 重新启动服务:关闭后浏览器页面失效,需在终端重新运行 jupyter notebook

  • 打开步骤

    1. 进入存放 Notebook 的目录。
    2. 点击相应的 .ipynb 文件,编辑界面即恢复。
  • 他人 Notebook:过程相同,先运行 jupyter notebook,进入目录点击文件即可。

  • 浏览器刷新显示页面无效,因后台服务器已关闭。
  • 注释无法加粗文字、超链接、标题或插入图片。
  • 仅限纯文本,信息传递受限。
  • 如Word等工具功能强大,但文件臃肿。
  • 打开Word文档远慢于记事本。
  • 轻量级标记语言,解决格式与文件大小问题。
  • 支持丰富样式,适合代码旁文档编写。
  • 在文字单元提升表达能力,结合Markdown使用。
  • 打开 Jupyter Notebook,将单元格切换为 Markdown 类型。
  • 输入 Markdown 内容后,点击工具栏运行按钮或按 Shift + Enter 快捷键,内容立即渲染展示效果。
  • 使用单个 # 创建:# 一级标题
  • 运行单元格后渲染为最大标题样式。
  • 标题层级:内容前加 # + 空格控制层级,一级用单个 #,最多六个 ###### 为最小标题。
  • 粗体:用两个 * 包围文字,如 **粗体**
  • 斜体:用单个 * 包围文字,如 *斜体*
  • 删除线:用两个英文波浪号 ~~ 包围文字(如 ~~删除线~~),注意用英文输入法的小波浪号,非中文大波浪号。
  • 所有符号:Markdown 中均使用英文输入法符号。
  • 纯文本:不加任何符号包围,为普通断行文字。
  • 换行规则:单次换行仅显示一个空格;分不同行需在文字间加空行,或编辑时多打一次换行。
  • 列表创建
    • 无序列表:在列表项前添加短横线 - 和空格。
    • 有序列表:在列表项前添加数字、英文句号 . 和空格。
  • 链接添加
    • 直接显示:直接输入网址,Markdown 会自动识别并将其转换为可跳转链接。
    • 自定义标题链接:使用 [标题](网址) 格式,可直观展示链接指向的内容,而非显示原始 URL。
    • 注意事项:完整的链接地址必须包含协议名(如 https://)。
  • 语法结构图片描述
    • 格式与链接非常相似,唯一区别是在最前方增加了一个英文感叹号 !
  • 图片描述的作用:当图片无法正常加载时,系统会显示该描述文字作为占位符,增强文档的健壮性。
  • 语法结构:使用右书名号 > 加空格,后面紧跟引用的内容。
  • 换行规则:引用块内的文字与普通段落一致,不会因为编辑时的换行而产生实际换行。
    • 强制换行:若需在引用中换行,需在上一行的末尾添加两个空格。
  • 语法结构:使用反引号 `` ` `` 包裹代码。
    • 反引号位于键盘左上角,与波浪号 ~ 在同一个键位。
  • 用途:专门用于在文字中插入代码片段,使其以等宽字体显示,便于阅读。
  • 三个反引号`` 包裹多行代码段落。
  • 语言高亮`python ````),自动应用语法高亮。
  • 数据分析公式:复杂公式(如求和、根号)键盘难输入,直接写代码不美观。
  • LaTeX 语法:支持根号、分数线等符号,提升公式表达直观性。
  • LaTeX 简介:排版系统,用于书籍、简历、论文等格式和布局。
    • 示例:左侧原始文档内容,右侧编译效果。
  • 公式写作:复杂公式用 LaTeX 编写。
  • Jupyter 使用:在 Markdown 中插入公式,只需掌握公式相关语法。
  • 加号 +、减号 -:直接用键盘符号。
  • 乘号:\times(times,相乘含义)。
  • 除号:\div(div,相除含义)。
  • 上标(如平方、三次方):^ 后跟内容(Shift + 6)。
  • 下标_ 后跟内容。
  • 默认限制:LaTeX 默认的上标 ^ 或下标 _ 只会作用于紧跟其后的一个字符。
  • 多字符组合:若需将多个字符作为整体处理,需使用花括号 {} 将其包裹。
    • 示例:x^{10}10 整体作为上标。
  • 求和与求根符号
    • 求和:使用 \sum
    • 求根:使用 \sqrt (square root)。
      • 示例:\sqrt[3]{x} 表示对 x 开 3 次方。
      • 根号内的长公式需用花括号 {} 括起,否则横线只会延伸至第一个字符。
  • 分数线
    • 使用 \frac{分子}{分母} 表示,frac 即 fraction 的缩写。
    • 示例:\frac{x+y}{x-y}
  • 语法结构:使用 \frac{分子}{分母}
    • frac 是 fraction(分数)的缩写。
  • 应用示例
    • 若要表示 $$\frac{x+y}{x-y}$,在 LaTeX 中输入 \frac{x+y}{x-y}`。
    • 第一个花括号 {} 存放分子内容,第二个花括号 {} 存放分母内容,系统会自动将其渲染为上下结构的数学分数形式。
  • 支持无数数学公式符号,可查阅符号表获取对应语法。
  • NumPy:常用于科学及工程领域。
  • 核心数据结构N 维数组(N 维数据)。
  • 1 维数组类似于 Python 列表。
  • 通过索引 [] 获取单个元素。
  • 通过切片 获取某范围多个元素。
  • 支持迭代 各个元素。
  • NumPy 数组:元素必须同一类型(如全数字或全字符串)。
  • Python 列表:无此类型限制。
  • 执行效率:大规模数学运算时,NumPy 数组速度远高于内置列表。
  • 提供大量数学运算函数,如求平均值标准差等。
  • 安装:NumPy 是第三方库,需在终端(Windows 下为 cmd,macOS 下为 Terminal)使用 pip install numpy(macOS 可能需 pip3 install numpy)进行安装。
  • 导入与别名
import numpy as np
- 习惯上使用 `as np` 给库起别名,目的是在调用函数时减少输入量,使代码更简洁。
  • 创建方法:使用 np.array() 方法将列表转换为 NumPy 数组。
  • 数组维度判断
    • 简单列表 → 1 维数组。
    • 嵌套列表(列表内含列表) → 2 维数组。
    • 简单判断:观察数组外层方括号的嵌套层数。
  • 强制转换:虽然 np.array() 可以接收不同类型的元素(如字符串、布尔值、浮点数),但它会强制将所有元素转换为同一类型(通常为字符串类型)。
  • 最佳实践:为了保证数据一致性与计算逻辑的正确性,建议从一开始就传入同类型元素。
  • ndim:返回数组的维度(如 1 或 2)。
  • shape:返回一个元组,表示各个维度中元素的个数。
    • 示例:对于 2 维数组,shape 返回 (行数, 列数)

import numpy as np

arr1 = np.array([1, 2, 3])

arr2 = np.array([[1, 2, 3], [4, 5, 6]])

print(arr1.ndim) # 输出: 1

print(arr2.ndim) # 输出: 2

print(arr1.shape) # 输出: (3,)

print(arr2.shape) # 输出: (2, 3)

  • 属性查询
    • size:返回数组中元素的总个数。
    • dtype:返回数组元素的类型,例如 int64 表示 64 位整数。
  • 快速创建数组的方法
    • np.zeros(n):创建长度为 n、元素全为 0 的数组。
    • np.ones(n):创建长度为 n、元素全为 1 的数组(注意:默认类型为浮点数,显示时带小数点)。
    • np.arange(start, stop, step):功能类似于 Python 内置的 range,用于创建数字序列数组。
      • 参数含义:起始值、结束值、步长。

NumPy 实践开始:导入与 arange 示例

Section titled “NumPy 实践开始:导入与 arange 示例”
  • np.arange(5, 10, 2) 返回 array([5, 7, 9])
  • 结束值不包含:10 不在数组中,因结束值不会被包含在范围内。
import numpy as np
  • 运行单元格以激活导入。
  • 下载课程资料中不带答案的 Notebook。
  • 先自行练习创建数组与查看属性,再验证答案。
import numpy as np
  • 必须导入,否则使用 np. 函数时报错。
  • 若运行时报 module not found error,在 cmd 或终端执行 pip install numpy
arr1 = np.array([元素列表])
  • 最直接方法:传入列表创建。
  • 交互环境(如 Jupyter)直接输入变量名显示结果,无需 print()
  • 定义:二维数组即数组中包含数组,通过嵌套列表创建。
  • 创建语法:使用 np.array() 并传入嵌套列表。
    • 示例:np.array([[1, 3, 5], [2, 4, 6]])
  • 注意事项:确保外层方括号内包含两个内层方括号,每个内层列表代表二维数组的一行。
  • Shift + Enter:运行当前单元格并跳转至下一个单元格,比手动点击工具栏运行按钮更高效。

元组在 NumPy 数组属性中的特殊表示

Section titled “元组在 NumPy 数组属性中的特殊表示”
  • 元组特性:NumPy 的 shape 属性返回一个元组,即使该元组仅包含一个元素,Python 也会在末尾添加一个逗号(如 (7,)),这是为了明确标识其为元组而非普通的括号数字。
  • 区分单一元素元组与数字
    • (7):会被解释为数字 7。
    • (7,):被明确识别为只有一个元素的元组。
  • 实际应用:在查看一维数组形状时,shape 返回 (n,),这清晰地表明它是包含 n 个元素的一维结构,而非单纯的数值。
  • 外层数组包含两个子数组

第二维度:每个子数组 3 个元素

Section titled “第二维度:每个子数组 3 个元素”
  • 进入第一个子数组,包含3 个元素,对应 shape 元组的第二个数字 3
  • 打印 .size 属性显示总数(如 6 个)。
  • 等同手动计数:数逗号数量 +1(一维:3 个;二维:6 个)。
  • 使用 .dtype 查看数组元素类型。
  • .dtype (数据类型)
    • 返回数组中元素的具体类型,例如 int64 表示 64 位整数。
    • 64 代表数据长度(比特),不同电脑配置可能导致输出结果(如 int32int64)存在差异,这属于正常现象。
  • np.zeros(n)
    • 创建一个长度为 n 的数组,所有元素初始化为 0
  • np.ones(n)
    • 创建一个长度为 n 的数组,所有元素初始化为 1
    • 注意:生成的元素默认带有小数点,表示其类型为浮点数(float)。
  • 变量命名:在 Jupyter Notebook 中,可以通过赋值语句(如 arr_all_0 = np.zeros(6))为生成的数组命名,以便后续调用。
  • 交互式显示:在 Jupyter 中,直接输入变量名即可查看数组内容,无需显式调用 print() 函数。
  • 命名规范:变量名中包含小数点(如 arr_all_0)在 Python 中是合法的,但需注意避免与浮点数语法混淆。
  • 生成的全1数组元素带有小数点,表示浮点数类型(float)。
  • 需求:10到20之间的偶数,包括20。
  • 语法:np.arange(10, 21, 2)
    • 起始值:10
    • 结束值:21(不包含,故用21确保20包含)
    • 步长:2(生成偶数)
  • 与内置 range() 参数一致,但针对 NumPy 数组。
np.arange(10, 21, 2)
  • 输出:[10 12 14 16 18 20]
  • NameError:直接调用 arange 报错 name 'arange' is not defined
  • 修正:添加 np. 前缀,即 np.arange()
  • 掌握:np.array()np.zeros()np.ones()np.arange() 等创建方法。
  • 属性:.shape.size.dtype 等基本查询。
  • 学习常见操作,如切片、数学计算等。
  • 将现有数组与新元素数组np.concatenate() 拼接成新数组
  • 参数形式:将数组放入列表中传参,例如 np.concatenate([arr1, arr2])
  • 支持无限长度列表,可一次性拼接多个数组。
  • np.zeros() 生成float64类型数组(元素带小数点)。
  • NumPy 数组数据类型必须统一,拼接结果自动转为浮点数。
  • 假设已有 arr1 = np.zeros(...)arr_new = np.array([新元素])
  • 调用:np.concatenate([arr1, arr_new]) → 返回新拼接数组。
  • 传入列表长度不限,非常方便处理批量拼接。
  • 单个元素:方括号内放一个数字(索引)。
  • 切片范围:方括号内放两个数字 start:end
    • start:范围开头索引。
    • end:范围结束索引(不包含该位置元素)。
    • 返回:从 startend-1 的所有元素。
  • 列表切片机制一致。
  • NumPy 数组强项:支持加减乘除等基本运算。
  • 规则:形状相同的一维数组,对应位置元素逐一运算,返回新数组
    • 示例:两个数组相加 → 相同位置元素相加的结果数组。
  • 列表对比:列表无法如此便捷实现逐元素运算。
  • NumPy 数组支持与单个数字的数学运算。
  • 广播机制 (Broadcasting):如 arr * 3,数字3会应用到数组每个元素,类似向量与标量运算。
  • 通过一组值计算得到单一值
    • max():求最大值
    • min():求最小值
    • sum():求总和
    • mean():求平均值
  • 调用:数组名.方法名() 直接返回结果。
  • 条件筛选 (Boolean Indexing)
    • 通过 arr[arr > 6] 语法,利用布尔数组对原数组进行索引。
    • 广播机制:比较运算符(如 > 6)会作用于数组的每一个元素,生成一个由 TrueFalse 组成的布尔数组。
    • 筛选原理:在方括号内放入布尔数组,NumPy 会自动筛选出对应位置为 True 的元素,返回一个包含这些元素的新数组。
  • 逻辑运算与复杂筛选
    • 若要实现多条件筛选(如“大于6且小于10”),需使用逻辑运算符的位运算版本:
      • 与 (AND):&
      • 或 (OR):|
      • 非 (NOT):~
    • 语法示例:arr[(arr > 6) & (arr < 10)]
    • 注意:每个条件表达式必须用圆括号 () 括起来,以确保逻辑运算的优先级正确。
  • 多条件筛选示例:(arr > 6) & (arr < 10)
  • & 为位运算与(and)符号,筛选符合所有条件的元素。
  • 每个条件用圆括号括起,确保优先级。
  • 本节Jupyter Notebook在课程配套文件下载。
  • 下节视频进行巩固练习
  • 导入:import numpy as np
  • 若要创建包含特定元素的数组,最直接的方法是使用 np.array() 函数并传入一个列表。
arr1 = np.array([1, 3, 2, 4, 9])
  • 创建全1数组:使用 np.ones() 方法,传入数组长度。
arr2 = np.ones(5)
  • 创建全0数组:使用 np.zeros() 方法,传入数组长度。
arr3 = np.zeros(3)
  • 提示:这些方法生成的数组元素默认为浮点数类型。
  • 排序两种方法:NumPy 的 sort 函数(不改变原数组) vs. 数组自身的 sort 方法(改变原数组)。
  • 此处要求改变原数组,使用 ale.sort()
  • 验证排序:运行后 print(ale),确认元素从小到大排序。
ale.sort()
print(ale)
  • 打印第三个元素:索引为 2,值为 2
print(ale[2]) # 输出: 2
  • 负索引:最后一个元素索引为 -1,倒数第二个为 -2,以此类推。
  • 打印第三个元素:使用 -3(倒数第三个),与正索引 2 相同。
print(ale[-3]) # 等同于 ale[2]
  • 切片操作:提取部分数组,语法 arr[start:end]
    • 第一个数字:起始索引(包含)。
    • 示例:从第二个元素到第四个元素(不包含第四个)。
print(ale[1:4]) # 第二个到第四个元素
  • 从索引 1(第2个元素)开始,到索引 4不包含第4个元素)。
  • 实际提取第 2、3、4 个元素(索引1、2、3)。
print(ale[1:4]) # 输出第2、3、4个元素
  • 两个数组 A1A2 长度均为 5
  • 直接逐元素相加,生成新数组。
  • 输出:L1 + L2
l1_plus_l2 = a1 + a2 # 或直接 print(a1 + a2)
print(l1_plus_l2)
  • NumPy数组支持元素级加减乘除,直接操作(如 arr1 + arr2)。
  • 前提:数组长度相同。
  • 列表需嵌套for循环,如 for e1 in list1for e2 in list2,逐元素相加,麻烦。
  • 任务:A2每个元素乘 -5
  • 直接:a2 * -5,无需循环。
a2 * -5
  • arr.max():求最大值
  • arr.min():求最小值
  • arr.sum():求和
  • arr.mean():求平均值
  • 直接输出只显示最后一个结果,需print每个才能看到所有
print(arr1.max())
print(arr1.min())
print(arr1.sum())
print(arr1.mean())

NumPy 布尔索引筛选 (速度数组示例)

Section titled “NumPy 布尔索引筛选 (速度数组示例)”
  • 原题目:从 speed 数组筛选 <5 或 >2 的数字(覆盖所有数字)。
  • 修改后:筛选 <1 或 >3 的数字。
  • 单条件测试:arr1 < 1 → 返回布尔数组(True/False,对应每个元素是否<1)。
  • 筛选应用:arr1[arr1 < 1] → 只保留**<1** 的元素。
arr1 < 1 # 生成布尔数组
arr1[arr1 < 1] # 布尔索引筛选
  • 空布尔数组ar1 < 1 全False,筛选结果为空数组,符合预期。
  • ar1 > 3:生成布尔数组,可筛选所有>3的数字。
  • 条件组合:用括号包围每个条件,中间用或符号 | 连接,筛选**<1 或 >3** 的数字。
(ar1 < 1) | (ar1 > 3) # 布尔数组
ar1[(ar1 < 1) | (ar1 > 3)] # 筛选结果
  • NumPy中OR逻辑使用竖线 | 表示,与Python内置 or 不同。
  • 语法:(条件1) | (条件2),每个条件需用括号包围。
(ar1 < 1) | (ar1 > 3) # 生成布尔数组
ar1[(ar1 < 1) | (ar1 > 3)] # 筛选<1 或 >3 的元素
  • AND逻辑使用**&**符号:arr[(arr < 1) & (arr > 3)]
  • 仅返回同时满足多个条件的元素,每个条件需括号包围。
(ar1 < 1) & (ar1 > 3) # 布尔数组
ar1[(ar1 < 1) & (ar1 > 3)] # AND筛选结果
  • Pandas基于NumPy构建,继承其高性能数组计算功能。
  • 优势:结合NumPy计算能力,适用于数据分析场景。
  • 安装:在终端或命令行中使用 pip install pandas (macOS 可能需要 pip3 install pandas),过程与安装 NumPy 一致。
  • 导入:惯例使用 import pandas as pd,通过别名 pd 调用库内方法,简化代码书写。
  • 定义:Series 是 Pandas 的基础数据结构,类似于 NumPy 的一维数组,但在处理标签数据时更具优势。
  • 创建 Series:使用 pd.Series() 方法,传入一个列表作为参数。
s1 = pd.Series([5, 17, 3, 26, 31])
  • 命名规范Series 首字母需大写,因为它本质上是一个类(Class)的构造函数。
  • Series 与 NumPy 数组的区别
    • Series 不仅展示元素内容,还会自动在左侧显示对应的索引(Index)。
    • 底部会显示 dtype,明确标识 Series 中元素的类型。
  • 独立获取
    • 使用 .values 属性获取所有元素值(返回 NumPy 一维数组)。
    • 使用 .index 属性获取所有索引信息(返回 RangeIndex 对象,包含 start、stop 和 step 信息)。
  • 操作逻辑:与 Python 列表和 NumPy 数组高度相似。
    • 索引访问s1[2] 返回索引为 2 的单个元素值。
    • 切片操作s1[1:3] 返回从索引 1 到 2 的部分 Series(不包含结束索引 3)。
  • 核心差异:索引操作返回单个元素值,而切片操作返回一个新的 Series 对象。
  • 自定义标签:在创建 Series 时,可以通过 index 参数为每个元素指定标签,不再局限于默认的整数索引。
s1 = pd.Series([5, 17, 3, 26, 31], index=["a", "d", "b", "c", "e"])
  • 索引显示:输出 Series 时,左侧将显示自定义的标签,而非默认的 0-4 数字。
  • 标签索引 (Label Indexing):使用自定义的标签名称直接访问元素。
    • 示例:s1['b'] 返回 3
  • 位置索引 (Positional Indexing):依然可以使用原始的整数位置进行访问。
    • 示例:s1[2] 返回 3
  • 区分概念:自定义的标签索引也被称为“标签索引”,它为数据访问提供了灵活性,同时保留了原有的位置索引功能。
  • 切片一致性:切片逻辑与列表、NumPy 数组相同,既支持标签切片,也支持位置切片。
    • 标签切片示例:s1['d':'c'](注意:标签切片通常包含结束位置)。
    • 位置切片示例:s1[1:3](不包含结束索引)。
  • 标签切片 (Label Slicing):使用自定义标签进行切片时,结束标签是包含在内的(例如 s1['d':'c'] 会包含标签 ‘c’ 对应的值)。
  • 位置切片 (Positional Slicing):使用整数位置进行切片时,遵循 Python 惯例,不包含结束位置(例如 s1[1:3] 不包含索引 3)。
  • 重要提醒:这种行为差异与 NumPy 和 Python 列表不同,需格外注意以避免逻辑错误。
  • 若要获取不连续或特定顺序的多个元素,可以在方括号内传入一个列表:
s1'a', 'e', 'c' # 按标签列表提取
s10, 4, 3 # 按位置列表提取
  • 这种方式不受原始顺序限制,可以随意调整返回元素的排列。
  • 当自定义索引本身为整数时(例如 index=[1, 3, 5, 7, 9]),直接使用 s1[3] 进行索引或切片容易产生歧义。
  • 歧义点:无法直观判断是指“标签为 3 的元素”还是“位置为 3 的元素”。
  • 解决方案:Pandas 提供了专门的方法来明确意图:
    • loc:强制使用标签索引进行取值或切片。
    • iloc:强制使用位置索引进行取值或切片。

s2.loc[3] # 获取标签为 3 的值

s2.loc[1:3] # 获取标签从 1 到 3 的切片(包含结束)

s2.iloc[3] # 获取位置为 3 的值

s2.iloc[1:3] # 获取位置从 1 到 2 的切片(不包含结束)

  • 问题核心:当自定义索引为整数时,使用方括号 s[3] 进行索引或切片会产生歧义,难以区分是指“标签为3的元素”还是“位置为3的元素”。
  • 解决方案:Pandas 提供了显式方法来明确意图,彻底消除歧义:
    • .loc:强制使用标签索引(Label-based)。
    • .iloc:强制使用位置索引(Integer-based)。
场景 .loc[索引] .iloc[索引]
单点取值 按标签查找 按位置查找
切片操作 包含结束标签 不包含结束位置
  • 修改 Series 中的值与字典操作类似,通过标签或位置直接赋值即可。
  • 最佳实践:为了避免与位置索引混淆,建议统一使用 .loc.iloc 进行修改。
s3.loc['青菜'] = 4.5 # 使用标签修改
s3.iloc[0] = 4.5 # 使用位置修改
  • 使用 in 关键字检查某个标签是否存在于 Series 的索引中,返回布尔值。
'青菜' in s3 # 返回 True 或 False
  • 通过字典创建:直接将字典传入 pd.Series() 构造函数,字典的键会自动成为 Series 的标签索引,值则对应数据。
s3 = pd.Series({'青菜': 4.1, '白萝卜': 2.2, '西红柿': 5.3})
  • Series 支持与 NumPy 类似的布尔索引筛选,通过传入布尔 Series 作为索引,可以过滤出符合条件的元素。
  • 筛选逻辑:s3[s3 > 5] 会返回一个仅包含满足条件元素的新 Series。
  • 在 Series 中进行多条件筛选时,可以使用逻辑运算符 & (与)、| (或)、~ (非)。
  • 重要规则:每个条件表达式必须用括号 () 包围,否则会因优先级问题导致报错。

s3[(s3 > 5) | (s3 < 3)]

- 逻辑运算符 `|` () `&` () Series 筛选中是必须的不能直接使用 Python 原生的 `or` `and` 关键字
  • 创建s1s1 = pd.Series([-1.2, 3.7, 8.5, -8.2, 6.3]),打印显示每个元素旁对应索引
  • 输出底部显示dtype: float64,表示浮点数类型,64bit长度。
s1.index # 返回所有索引
s1.values # 返回所有元素值(NumPy数组)
  • 创建示例:元素[1,2,3,4,5],索引从5到1反向指定。
s = pd.Series([1, 2, 3, 4, 5], index=[5, 4, 3, 2, 1])
  • 效果:左侧索引显示5,4,3,2,1,不再是默认0,1,2,3,4
  • 后续:取出指定元素任务。
  • 陷阱示例:自定义索引[5,4,3,2,1]对应元素[1,2,3,4,5],位置第二个元素为2
  • 直接s[1]标签索引取值,返回5(标签1对应5),非预期位置值2。
  • 原因:Series同时支持标签索引与位置索引,易混淆(讲者承认设计此陷阱)。
  • 解决方案:用.iloc[1]明确位置索引,返回2
s.iloc[1] # 返回位置1的元素:2
  • 建议:索引取值始终用.loc(标签)或.iloc(位置),避免歧义。
  • 位置索引成功:使用 .iloc[1] 提取第二个位置元素 2
  • 标签切片s.loc[5:3] 获取标签 5,4,3 对应的前三个元素。
  • 切片陷阱
    • 位置切片.iloc):结束值不包含
    • 标签切片.loc):结束值包含在内。
  • 字典创建:直接传入字典到 pd.Series()键自动成为标签索引
    • 方法不变,仍用 pd.Series()
  • 输入提示:打中文需切换输入法,但标点符号仍用英文
s = pd.Series({5: 1, 4: 2, 3: 3}) # 键为标签
  • 当前状态:已创建字典初始化的 Series。
  • 更新操作:通过索引将小张对应值改为95,再输出新Series
s3["小张"] = 95 # 或类似赋值
print(s3)
  • 视觉确认小张值变为95
  • 推荐使用&;.loc:明确标签索引,避免方括号歧义(S3中文标签清晰,S2整数标签易混淆)。
s3.loc["小张"] = 95
print(s3)
  • 任务:输出 S3 中所有大于80 且小于90的元素。
  • 布尔筛选原理
    • s3 > 80 返回布尔 Series,每个位置对应原标签True 表示原值 >80,False 表示 ≤80。
  • 完整条件:用 &(与)组合两个条件,每个用括号包围:
condition = (s3 > 80) & (s3 < 90)
result = s3[condition]
  • 自动对齐机制:对两个 Series 进行加减乘除运算时,Pandas 会根据索引 (Index) 自动对齐。
  • 缺失值处理
    • 若某个索引只存在于其中一个 Series 中,运算结果在该位置会显示为 NaN (Not a Number)。
    • NaN 表示无法得到计算值。
  • 运算示例

result = s1 + s2

  • 方法替代运算符:当需要为缺失值指定默认值时,不能使用符号(如 +),必须使用 .add() 等方法。
  • fill_value 参数:在方法中传入 fill_value 参数,可以指定缺失位置的填充值。
  • 优势:允许在计算过程中进行额外的参数填充,避免直接产生 NaN

s1.add(s2, fill_value=0)

- **运算一致性**此逻辑同样适用于减法 (`sub`)、除法 (`div`) 等其他算术运算
  • NumPy 兼容性:Pandas Series 对象支持与 NumPy 数组相同的统计方法,能快速获取核心数据指标。
方法 功能
.max() 获取最大值
.min() 获取最小值
.sum() 获取总和
.mean() 获取平均值
  • 功能:提供 Series 数据的全面统计摘要,是 NumPy 中没有的特有方法。
  • 输出内容:包含元素个数 (count)、平均值 (mean)、标准差 (std)、最小值 (min)、四分位数 (25%, 50%, 75%) 及最大值 (max)。
  • 数据类型:输出结果包含 dtype 信息(如 float64)。
  • 逐元素运算:当对 Series 与单个数字进行算术运算(如 s1 * 3)时,Pandas 会自动将该操作应用到 Series 中的每个元素,无需手动循环。
  • 广播原理:此机制与 NumPy 的广播一致,极大地简化了批量数据处理。
  • 复杂逻辑处理:当需要根据 Series 的值进行复杂分类(如成绩评级)时,不能直接使用简单的数学运算。
  • 自定义函数映射
    • 定义一个处理单个数值的函数(如 get_grade_from_score)。
    • 编写逻辑判断(if-elif-else)来返回对应的分类结果。
    • 只要定义好该函数,即可将其应用到 Series 的每个元素上,实现基于分数的等级转换。
  • 场景:当需要对 Series 中的每个元素应用复杂逻辑(如成绩等级分类)而无法通过简单的数学运算实现时,可以使用 apply() 方法。
  • 核心机制
    • apply() 接收一个函数作为参数。
    • 它会自动遍历 Series 中的每个元素,将该元素作为参数传入函数中进行调用。
    • 返回一个新的 Series,其元素为函数调用的结果。
  • 代码示例

def get_grade_from_score(score):

if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "D"

grades = scores.apply(get_grade_from_score)

- **注意事项**
- `apply()` 不会改变原始 Series而是返回一个新的 Series
- 对于简单的逻辑也可以直接使用 `lambda` 匿名函数作为 `apply()` 的参数例如 `scores.apply(lambda x: x*x)`
- `apply()` 极大增强了操作 Series 的灵活性
  • 导入Pandas:使用 import pandas as pd
  • 创建Series:向 pd.Series() 传入列表,即可得到对应Series。
  • 练习准备:下载课程资料中的Not Do(不带答案版),暂停练习后验证答案。
  • 后续内容:更多Series操作练习。
  • 指定索引创建:使用 index= 参数传入索引列表,直接创建对应标签的 Series。
  • 打印验证:创建 S1 和 S2 后,使用 print() 输出确认结构。
  • 相同索引相加:直接 s1 + s2 实现相同索引位置元素相加。
  • 索引对齐规则:Series 默认按索引对齐进行运算。
  • NaN 产生原因:若某个索引只存在于一个 Series 中,对应位置产生 NaN(缺失值)。
  • 直接运算的局限性:使用 + 等算术运算符时,若索引无法对齐,Pandas 会产生 NaN (Not a Number) 值,表示无法计算。
  • 使用 add() 方法处理缺失值
    • 若要避免 NaN,应使用 .add() 等算术方法替代运算符。
    • 通过 fill_value 参数可以为缺失值指定默认值(如 0),从而实现补齐计算。
    • 示例:s1.add(s2, fill_value=0) 会将缺失的索引位置视为 0 进行加法运算。
  • 适用范围:此逻辑同样适用于 sub() (减法)、mul() (乘法) 和 div() (除法) 等方法。
  • 单独调用方法s1.max() 获取最大值,s1.min() 获取最小值。
  • 便捷方法s1.describe() 一行输出完整统计摘要。
  • 输出指标
    • count:元素个数
    • mean:平均值
    • std:标准差
    • min:最小值
    • 25%:第一四分位数
    • 50%:中位数
    • 75%:第三四分位数
    • max:最大值
  • 需求:根据分数 Series 计算对应的等级(90分以上 A,80-90分 B,70-80分 C,70分以下 D)。

  • 解决方案

    1. 定义一个普通 Python 函数,根据传入的分数返回对应的等级字符串。
    2. 使用 apply() 方法将该函数映射到 Series 的每一个元素上。
  • 关键点

    • apply() 接收函数名作为参数,返回一个新的 Series,不会改变原始数据。
    • 若逻辑简单,可配合 lambda 匿名函数使用,语法为 lambda 参数: 返回值
    • 这种方式比手动循环遍历 Series 更高效且灵活。
  • 标量运算:当 Series 与单个数值(标量)进行运算时,该操作会自动应用到 Series 的每一个元素上,无需显式循环。
  • 函数定义:使用 def 定义一个处理分数的函数,通过 if-elif 逻辑链判断分数区间并返回对应的等级字符串(A/B/C/D)。
  • 逻辑优化:在 elif 判断中,由于前面的条件(如 score >= 90)若满足会直接 return 结束函数,后续的判断(如 score >= 80)默认隐含了 score < 90 的条件,因此无需显式使用 and score < 90 进行区间限制。
  • 代码简化
def get_grade(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "D"
  • 测试get_grade(85) 返回 “B”get_grade(55) 返回 “不及格”
  • 应用到S1s1.apply(get_grade) 将函数映射到 S1 每个元素,生成新等级 Series。
  • apply特性:相当于高级的逐元素函数应用,不会修改原 S1。
  • 无括号传递:传入函数名时不要加括号 (),因为不是传递函数调用结果,而是传递函数本身,让 apply() 对每个 Series 元素自动调用。
  • 运行演示s1.apply(get_grade) 输出新 Series:每个成绩对应等级(如 A/B/C/D),旁边保留原始标签/索引,便于追踪原值对应关系。
  • Pandas 最强大结构DataFrame(下一视频学习)。
  • 数据结构层级:DataFrame 是由多个 Series 组成的二维数据结构,类似于表格。
  • 核心优势:相比 NumPy 的二维数组(要求数据类型必须一致),DataFrame 的不同列可以存储不同的数据类型,是数据分析中最常用的结构。
  • 索引与列名:不仅拥有行索引(Index),还拥有列名(Columns),使其更像是一个由多个 Series 组成的字典。
  • 结构类比:可以理解为以列名为键、Series 为值的字典结构。
  • 方法:使用 pd.DataFrame() 函数。
  • 参数传递:传入一个字典,其中键为列名,值为对应的 Series 数据。

df = pd.DataFrame({“学号”: s_id, “班级”: s_class, “成绩”: s_grade})

- **输出效果**执行后会生成一个排版整齐的表格包含行索引0, 1, 2...和指定的列名
  • 数据源多样性:除了使用 Series 字典创建 DataFrame,还可以直接传入列表 (List) 作为数据源。
  • 索引对齐:DataFrame 默认的行索引(Index)与传入的 Series 索引保持一致;若传入列表,则默认从 0 开始递增。
  • 保留标签索引:若传入的 Series 本身带有自定义标签索引,DataFrame 会自动继承这些标签作为行索引,保持数据关联性。
  • 多层级数据结构:可以通过传入“嵌套字典”(字典中包含字典)来一次性创建包含索引和列名的 DataFrame。
  • 结构映射
    • 最外层字典的键(Key)对应 DataFrame 的 列名 (Columns)
    • 内层字典的键对应 DataFrame 的 行索引 (Index)
    • 内层字典的值对应具体的 数据值 (Values)
  • 查看索引:使用 df.index 获取所有行索引标签。
  • 查看列名:使用 df.columns 获取所有列名。

df4 = pd.DataFrame({

"学号": {"小明": "01", "小红": "02"},
"班级": {"小明": "一班", "小红": "二班"}

})

  • 获取原始数据:使用 .values 属性可获取 DataFrame 中的所有数据,返回类型为 NumPy 数组,这意味着所有针对 NumPy 数组的操作(如切片、聚合运算)均可直接作用于该属性。
  • DataFrame 转置:使用 .T 属性(注意是大写字母 T)可实现行列互换,即行变列、列变行。
  • 方括号提取:使用 df['列名'] 即可提取对应列,返回类型为 Series,且该 Series 会自动继承 DataFrame 的行索引(Index)。
  • 点号提取:除了方括号,也可以使用 df.列名 的语法来获取列数据,这在列名符合变量命名规则时更为简洁。
  • 属性访问法:由于每列 Series 实际上是 DataFrame 的属性,可以直接使用 df.列名 获取,效果与 df['列名'] 一致。
  • 列名限制:若列名包含空格或特殊符号,则无法使用点号属性访问,此时必须使用方括号 df['列名']
  • 列表传入:在方括号内传入一个包含多个列名的列表(例如 df'列名1', '列名2'),即可提取多个列。
  • 返回值类型:提取多列后返回的是一个 DataFrame(而非 Series),因为结果由多个 Series 组成。
  • 索引定位:提取列使用列名,提取行则应使用索引。
  • loc 与 iloc
    • .loc['标签']:根据行标签索引提取。
    • .iloc[位置]:根据行位置(整数)索引提取。
  • 行数据类型:提取出的单行数据类型为 Series,这与提取单列数据的逻辑一致。
  • 切片规则
    • iloc (位置索引):切片遵循 Python 惯例,不包含结束位置(左闭右开)。
    • loc (标签索引):切片包含结束标签(闭区间)。
  • 多行提取
    • 若需提取不连续的行,可向 lociloc 传入一个包含多个标签或位置的列表。
    • 示例:df.loc'小明', '小红'df.iloc0, 2
  • 行列精准定位
    • lociloc 的方括号内使用逗号分隔两个参数:第一个表示行,第二个表示列。
    • 格式:df.loc[行标签, 列名]df.iloc[行位置, 列位置]
  • 应用场景:此方法用于获取表格中特定单元格的数值,返回结果为该单元格的具体值(如字符串或数字)。
  • 方括号嵌套:若要提取多列,需使用双重方括号 df'列名1', '列名2',这在逻辑上等同于传入一个包含列名的列表,返回的是一个 DataFrame 对象。
  • 语法df.loc[行切片, 列切片]df.iloc[行切片, 列切片],第一个参数行切片,第二个列切片,可截取表格任意部分。
  • 切片规则:标签切片(loc)包含结束值;位置切片(iloc)不包含结束值(左闭右开)。
  • 保留所有行提取部分列df.loc[:, '列名']df.iloc[:, 列位置],省略切片前后值,直接用冒号&;: 表示全部范围。
  • 列表指定df.loc[[标签1, 标签2], '列名']df.iloc[[位置1, 位置2], 列位置],通过位置或标签传入列表提取不相邻行/列。
  • 灵活性:支持切片与列表混合,截取任意子集。
  • 筛选原理:通过传入一个布尔 Series 作为行索引,DataFrame 会返回所有对应值为 True 的行。
  • 筛选语法df[df['列名'] > 阈值]
  • 行与列的筛选差异
    • 筛选是基于条件逻辑,返回符合条件的行子集。
    • 筛选是基于列名,返回特定的列数据。
  • 数据分析意义:在数据分析流程中,行通常代表一个实例(如学生、城市),列代表属性(如身高、人口)。筛选符合特定条件的行(如“成绩大于80分的学生”)是提取符合业务逻辑的实例,这比单纯提取某一列属性更为常见且实用。

df[df[‘成绩’] > 80]

  • 逻辑相似性:DataFrame 的条件筛选逻辑与 NumPy 数组及 Pandas Series 的布尔索引完全一致,利用了相同的广播机制进行逐元素比较。
  • 筛选机制:通过传入一个布尔 Series 作为行索引,DataFrame 会保留所有对应值为 True 的行。
  • 代码语法df[df['列名'] > 阈值]
  • 行筛选的意义:行通常代表数据实例(如学生),列代表属性(如成绩)。筛选行是为了提取符合业务逻辑的实例,这比筛选列更具分析价值。
  • 一致性:该筛选逻辑与 NumPy 数组及 Pandas Series 的布尔索引完全一致,利用广播机制进行逐元素比较。

逻辑运算符在 DataFrame 筛选中的应用

Section titled “逻辑运算符在 DataFrame 筛选中的应用”
  • 多条件组合:可结合逻辑运算符 & (与)、| (或)、~ (非) 进行复杂筛选。
  • 注意事项
    • 每个条件必须用括号 () 包围,以确保运算优先级正确。
    • 逻辑运算符与 Python 原生 and/or 不同,需使用位运算符。
  • 示例

筛选成绩大于80且班级为三班的行

Section titled “筛选成绩大于80且班级为三班的行”

df[(df[‘成绩’] > 80) & (df[‘班级’] == ‘三班’)]

  • head() 方法:用于快速查看 DataFrame 的前 N 行数据。
  • 用途:在处理大型数据集时,通过 df.head() 快速检查数据结构和内容,避免直接打印全部数据造成界面混乱。
  • 示例
df.head() # 默认返回前5行
df.head(2) # 返回前2行
  • 用法df.head(n),通过参数 n 指定查看前 n 行。
  • 场景:实际数据常达几千、几万甚至几十万行,直接打印易导致界面混乱。
  • 价值:快速查看开头几行,了解数据包含的信息及各列变量特点。
df.head(2) # 查看前2行
  • 下载课程资料的无答案 Notebook,先自行练习创建 DataFrame 及操作,再验证答案。
  • 第一步:熟悉 import pandas as pd 引入。
  • 语法pd.Series(数据列表, index=标签列表)
  • 示例
s = pd.Series(['数据1', '数据2'], index=['小陈', '小李'])
  • 方法:多个 Series 分别作为 DataFrame 的列。
  • 注意:代码中引号、逗号使用英文标点(非中文)。
  • index 参数为列表,顺序一一对应数据元素。
  • 示例:标签 ['小陈', '小李'] 对应数据 ['值1', '值2']

分类变量标签指定实践 (gender & hide)

Section titled “分类变量标签指定实践 (gender & hide)”
  • 使用相同方法指定标签。
  • 标签顺序反转:从 006 到 001(屏幕显示 001 到 006)。
  • 使用相同方法。
  • 类型为分类数
  • 标签范围:001 到 005(故意少给一个数据)。
  • 输入后必须运行,否则变量不会被创建。
  • 构建逻辑:通过 pd.DataFrame() 传入一个字典,其中键(Key)为列名,值(Value)为对应的 Series 对象。
  • 标签自动对齐:当使用多个 Series 创建 DataFrame 时,Pandas 会自动根据索引(index)标签进行对齐。
  • 数据缺失处理:若某些标签在某个 Series 中缺失,Pandas 会自动将其标记为 NaN(Not a Number),处理方式与 Series 运算一致。

示例:将多个 Series 合并为 DataFrame

Section titled “示例:将多个 Series 合并为 DataFrame”

df = pd.DataFrame({‘姓名’: name, ‘性别’: gender, ‘身高’: height})

  • 查看数据:使用 df 直接输出整个表格的内容。
  • 数据对齐验证:通过观察输出结果,检查不同 Series 的标签是否正确对齐,以及缺失数据是否被正确识别为 NaN
  • 自动对齐原理:当使用多个 Series 创建 DataFrame 时,Pandas 会根据索引(index)标签自动进行对齐。
  • 数据缺失处理:若某个标签在某个 Series 中缺失,Pandas 不会报错,而是自动将其标记为 NaN (Not a Number)。
  • 对齐示例

即使 Series 的索引顺序不同或长度不一,Pandas 也会根据标签自动匹配

Section titled “即使 Series 的索引顺序不同或长度不一,Pandas 也会根据标签自动匹配”

students = pd.DataFrame({‘姓名’: name, ‘性别’: gender, ‘身高’: height})

- **观察输出**通过直接打印 DataFrame可以清晰地看到不同 Series 的标签对齐结果以及缺失值NaN的填充位置
  • 标签顺序不一致:即使传入的 Series 索引顺序不同(例如一个为 001-006,另一个为 006-001),Pandas 依然能根据标签正确拼凑出每一行数据。
  • 索引对齐逻辑:创建时,Pandas 会取所有 Series 索引的并集作为 DataFrame 的索引,未匹配到的位置自动填充 NaN
  • 行索引df.index 获取 DataFrame 的行索引。
  • 列名df.columns 获取 DataFrame 的列名。
  • 语法df.T属性,不加括号)。
  • 效果:行变为列,列变为行。
df.T # 直接输出转置结果
  • NaN:Not a Number,表示不存在真实值(缺失数据)。
  • 语法df.列名(如 df.名字.身高)。
  • 效果:直接输出身高数据。
  • 语法df['列名'](如 df['名字'])。
  • 优势:更通用,适用于列名含空格情况(如“身高 空格”)。
  • 原因:点号法无法识别空格为列名一部分。
  • 输入姓名后加冒号,同时输出性别和身高。
  • 语法结构:在 df 后使用方括号 [],内部传入一个包含多个列名的列表。
  • 代码示例

df’性别’, ‘身高’

- **关键点**方括号内必须嵌套一个列表`[[]]`),否则会因为参数数量不匹配而报错

DataFrame loc 列表提取介绍 (非连续行/列)

Section titled “DataFrame loc 列表提取介绍 (非连续行/列)”
  • 非连续行问题:003和005不挨着,不能用 003:005,否则会包含004。
  • 非连续列问题:姓名和身高不挨着,也无法用切片。
  • 使用 loc 传入两个列表
    • 第一个列表:行标签(如 [003, 005])。
    • 第二个列表:列名(如 ['姓名', '身高'])。
df.loc[ [行列表], [列列表] ]
  • 优势:loc 和 iloc 很万能,能提取任意指定位置。
  • 场景:提取行标签 003, 005所有列(全变量)。
  • 切片语法df.loc[['003', '005'], '姓名':'身高']
  • 省略冒号前:默认从首列(姓名)开始。
  • 省略冒号后:默认到末列(身高)结束。
  • 全列简写df.loc[['003', '005'], :]
df.loc[['003', '005'], :] # 输出 003,005 行的所有列
  • 优势:无需逐列指定,高效输出完整行数据。
  • 场景:当需要根据特定条件(如身高大于165)筛选 DataFrame 中的行时,可以使用布尔 Series。
  • 实现逻辑
    • 表达式 students['身高'] > 165 会生成一个布尔 Series,其中每一行对应 TrueFalse
    • 将此 Series 传入 students.loc[] 或直接作为索引,即可筛选出符合条件的行。

students.loc[students[‘身高’] > 165]

- **特点**
- 返回结果是一个新的 DataFrame仅包含满足条件的行
- 这种方式非常直观适合在数据分析过程中根据业务逻辑快速提取子集
  • 逻辑组合:可以使用 & (AND) 和 | (OR) 运算符结合多个条件。
  • 语法注意事项
    • 每个条件表达式必须用圆括号 () 包裹,以确保正确的运算优先级。

示例:筛选身高大于 165 且性别为女的学生

Section titled “示例:筛选身高大于 165 且性别为女的学生”

students.loc[(students[‘身高’] > 165) & (students[‘性别’] == ‘女’)]

- **应用价值**在复杂的数据分析任务中能够灵活利用逻辑运算符进行多维度筛选是高效提取信息的关键

DataFrame 条件筛选:缺失值处理与 & 连接

Section titled “DataFrame 条件筛选:缺失值处理与 & 连接”
  • 缺失值在比较运算中按 False 处理。
  • 符合预期:缺失值不会出现在筛选结果中。
  • 将条件表达式放入 students.loc[条件] 的方括号内。
  • 效果:仅保留条件为 True 的行。
  • 第二个条件:students['性别'] == '女',生成布尔 Series。
  • 多条件用 & 符号连接。
  • 每个条件需用圆括号 () 包裹,确保运算优先级。

DataFrame head() 方法与筛选验证实践

Section titled “DataFrame head() 方法与筛选验证实践”
  • 示例(students['身高'] > 165) & (students['性别'] == '女')
  • 语法:用 & 连接,每个条件必须用括号包围
  • 输出结果:仅筛选出小李(符合身高>1.6米且女性条件)。
  • 作用:快速查看数据开头
  • 语法df.head(n),显示前n行
students.head(5) # 显示前5行
students.head() # 默认前5行
  • 优势:无需参数即可预览,便于快速检查数据集结构。

DataFrame 高级操作预告与数据分析实践

Section titled “DataFrame 高级操作预告与数据分析实践”
  • 数据分析过程中,DataFrame 的操作非常频繁,掌握其高级用法能极大提升开发效率。
  • 后续学习将深入探讨更多针对 DataFrame 的实用操作技巧。
  • 方法:通过重新赋值(Re-assignment)更新某一列的数据。
  • 语法df['列名'] = 新的Series
  • 注意事项
    • 若 DataFrame 具有标签索引(Index),被赋值的 Series 也必须包含对应的索引属性。
    • 确保新 Series 的索引与 DataFrame 的索引匹配,以保证数据的正确对齐。

df1[‘成绩’] = pd.Series([90, 91, 83, 95, 94, 85], index=[‘001’, ‘002’, ‘003’, ‘004’, ‘005’, ‘006’])

DataFrame 列更新:列表与标签对齐

Section titled “DataFrame 列更新:列表与标签对齐”
  • Series赋值:需标签对齐,否则产生缺失值
  • 列表赋值自动按顺序对齐,无需标签,长度匹配DataFrame行数即可。
  • 不存在列名时不报错,自动新增列,列名为指定名称。
  • 通过loc/iloc提取行后同样可更新
  • Series赋值行:需标签与列名对齐,否则缺失值。
  • 列表赋值行按顺序对齐,更省事。

df[‘新列’] = [值1, 值2, …] # 自动顺序对齐,或创建新列

df.loc[0] = [新值序列] # 列表按列顺序对齐

  • 语法df.drop(行标签) 删除指定行。
  • 多行删除:传入标签列表 df.drop(['标签1', '标签2'])
  • 语法df.drop('列名', axis=1)
  • axis=1 表示操作列(默认 axis=0 操作行)。
  • drop() 仅返回新 DataFrame不修改原数据
  • 永久删除:重新赋值 df = df.drop(...)

df = df.drop(‘标签名’)

df = df.drop([‘标签1’, ‘标签2’])

df = df.drop(‘列名’, axis=1)

  • 轴的概念:DataFrame 是二维结构,可以看作由两个轴组成:
    • axis=0:垂直方向,即纵向(行)。
    • axis=1:水平方向,即横向(列)。
  • 默认行为:大多数 DataFrame 方法(如 drop)默认 axis=0,即默认对行进行操作。
  • 统计与操作一致性:当沿纵向(axis=0)操作时,数据通常具有相同的含义和类型(如所有身高数据均为浮点数),这使得统计计算更为合理。
  • 删除行df.drop('标签')(默认 axis=0)。
  • 删除列df.drop('列名', axis=1)
  • 逻辑记忆:将 axis 理解为“操作方向”,axis=0 是沿着索引向下,axis=1 是沿着列名向右。
  • 灵活性:由于 DataFrame 的二维特性,明确指定 axis 可以消除操作歧义。
  • 统一接口:许多后续的统计和数据处理方法都会频繁使用 axis 参数,掌握此概念是与 Pandas 交互的基础。
  • 列名对齐:相同列名(如A、B、C)直接对应计算,不同列(如DF1无D)在结果中填充NaN
  • 索引对齐:保留双方所有标签(如DF1: 001、003、005、007;DF2: 012),不匹配位置产生NaN
  • DF1 + DF2:
    • 共同列A、B、C正常计算。
    • DF1独有列或DF2独有D列全为NaN。
    • 索引合并后,不对齐处为NaN。
result = df1 + df2 # 自动对齐列名与索引
  • 类似Series + Series的索引对齐逻辑,扩展到二维结构同时处理行索引 + 列名

DataFrame 算术运算的 fill_value 参数

Section titled “DataFrame 算术运算的 fill_value 参数”
  • 问题背景:直接进行 df1 + df2 运算时,若索引或列名无法对齐,结果会产生 NaN(缺失值)。
  • 解决方案:使用 add() 方法替代 + 运算符,通过 fill_value 参数指定填充值。
    • 语法:df1.add(df2, fill_value=0)
    • 作用:在运算前,将无法对齐的位置填充为 0,从而避免结果中出现大面积 NaN
  • 双重缺失场景:如果某个位置在两个 DataFrame 中都不存在(即双方都为 NaN),即使设置了 fill_value=0,该位置的计算结果仍为 NaN
    • 原因:因为该位置在两个数据源中均无有效数值,无法进行加法运算,因此无法进行替换。
  • 除了 add() 方法,Pandas 同样支持 sub() (减), div() (除), mul() (乘) 等方法。
  • 这些方法与 add() 一样,均接受 fill_value 参数,用于处理运算中因索引或列名不匹配产生的缺失值。
  • 广播机制:当 DataFrame 与 Series 进行运算时,Pandas 会自动进行对齐。
  • 对齐规则
    • Series 的索引会与 DataFrame 的列名进行对齐。
    • 运算会应用到 DataFrame 的每一行上。
    • 对齐失败的位置(即索引与列名不匹配)会产生 NaN
  • 这种操作是 NumPy 广播机制在 Pandas 中的体现,允许不同形状的数据结构进行高效的逐元素运算。

  • 运算过程:

    1. 匹配 Series 的索引与 DataFrame 的列名。
    2. 将该 Series 的操作广播到 DataFrame 的每一行。
    3. 无法对齐的部分填充为 NaN
  • DataFrame 与单个数字运算时,操作自动广播到每一项数据。
  • 确保操作适用于所有数据类型:DataFrame可能含多种类型,同一操作在不同类型上效果不同或报错
  • 解决方案:先提取类型一致的部分DataFrame再运算。
import pandas as pd # 每次都需要导入
  • 复制数据到中间变量 buildendata
  • students = pd.DataFrame(buildendata)
  • 字典外层键自动成为列名
  • 外层键 → 列名:如 ‘考试1’、‘考试2’ 成为列。
  • 内层键 → 行索引:如 ‘001’、‘002’ 成为索引。
  • 问题:需求要求学号为索引,但当前结构反转(学号成列名)。
  • 使用 students.T 将行和列互换。
  • 转置后:学号成为索引,考试科目成为列名。
students = pd.DataFrame(student_data).T # 转置并赋值
  • 转置后结构符合需求:学号为索引,科目为列。
  • 先指定列名(如 ‘考试4’)。
  • 列表直接赋值,按行索引顺序依次填充数据。
students['考试4'] = [80, 90, 85] # 按索引 '001','002','003' 顺序添加
  • 若用 Series 赋值,因索引不匹配(Series 索引如 ‘002’,‘003’),未匹配行变 NaN
  • 示例:考试4 列全为 NaN。
print(students)
  • 新列成功添加,DataFrame 扩展为包含 ‘考试4’ 的完整结构。
  • 默认无法对齐,需设置 index=[001, 002, ...]Series 索引匹配 DataFrame。
  • .loc["007"]获取行、更新行、添加行
  • 标签 007 不存在时,等同添加新行,直接赋值列表
students.loc["007"] = [列表数据] # 添加行
  • 使用 .loc["007"] = [80, 75, 90, 85] 添加新行。
  • 运行后,新行成功出现。
students.loc["007"] = [80, 75, 90, 85] # 标签 007 不存在,执行添加操作
  • 使用 drop(列表, axis=0) 删除指定行。
  • axis=0:沿索引纵向操作(删除行)。
  • axis=1:沿列名横向操作(删除列)。
students.drop(["006", "007"], axis=0) # 删除多行
  • 二维结构轴线:DataFrame 可看作拥有两个轴线,纵向(行)为 axis=0,横向(列)为 axis=1
  • 默认参数:大多数 DataFrame 方法默认 axis=0,因为沿纵向操作时,同列数据通常含义一致(如均为身高),利于统计。
  • 删除操作差异
    • drop(..., axis=0):沿索引纵向操作,删除行。
    • drop(..., axis=1):沿列名横向操作,删除列。
  • 自动对齐:运算时(如 +, -, *, /)会根据列名和索引标签进行自动对齐,缺失部分默认填充为 NaN
  • fill_value 参数:使用 add(), sub(), mul(), div() 等方法时,可指定 fill_value=0 替代缺失值,但若两边该位置均为 NaN,结果仍为 NaN
  • 广播机制 (Broadcasting)
    • DataFrame 与 Series:Series 的索引与 DataFrame 列名对齐,运算应用至每一行。
    • DataFrame 与标量:数值运算直接应用至 DataFrame 的每一个元素,需确保数据类型一致以避免报错。

DataFrame drop 练习结果与 axis=1 指定

Section titled “DataFrame drop 练习结果与 axis=1 指定”
  • 执行 drop(["006", "007"])(默认 axis=0),结果中这两行消失。
  • 原始&;students&;不变drop 返回新 DataFrame,未修改原数据。
  • 下一题:drop(["考试2", "考试4"], axis=1) 删除指定列。
  • Bonus 表示考试加分:考试一全班+2分,考试二+3分,以此类推。
  • 使用 pd.Series() 创建,索引匹配 DataFrame 列名
bonus = pd.Series({ "考试一": 2, "考试二": 3, ... }) # 键为列名,值为加分
  • students + bonus广播加分到对应考试列。
  • 每个学生的该列分数自动增加对应加分值。
result = students + bonus # 输出加分后结果
  • 场景:老师见考试四成绩太差,整体提升10分

  • 步骤

    1. 提取列:exam4 = students['考试四'](得到Series)。
    2. 广播加分:exam4 + 10(每项统一+10)。
    3. 赋值更新:students['考试四'] = exam4 + 10
exam4 = students['考试四']
students['考试四'] = exam4 + 10 # 72 → 82
  • 效果:直接修改原DataFrame对应列,所有考试四分数增加10。

Jupyter Notebook 交互式执行与结果重置

Section titled “Jupyter Notebook 交互式执行与结果重置”
  • 交互式执行风险
    • 反复运行同一个单元格会导致数据(如分数)被重复累加。
    • 逻辑一旦执行多次,结果会偏离预期,导致数据混乱。
  • 重置与清理
    • 当数据状态混乱时,点击工具栏中的 Restart & Run All 按钮(重启内核并重新运行所有代码块)。
    • 该操作会清空当前内存中的所有变量,并从头开始依次执行所有代码块,确保数据回到初始状态。
  • 最佳实践
    • 尽量避免对单一单元格进行反复执行操作。
    • 通过重启内核来验证代码逻辑的健壮性,确保无论运行多少次,最终结果均保持一致。
  • Series 类似,支持 max()min()sum()mean() 等。
  • 二维特性:指定操作轴向
    • axis=0默认):纵向,计算各列统计值(沿行方向)。
    • axis=1横向,计算各行统计值(沿列方向)。
  • df.mean():默认 axis=0,返回各列均值Series)。

DataFrame 统计方法的 axis 参数应用

Section titled “DataFrame 统计方法的 axis 参数应用”
  • 横向操作 (axis=1)
    • 当指定 axis=1 时,计算方向变为横向(沿列名方向)。
    • 此时函数会对每一行数据进行统计(如计算每个学生的平均分)。
    • 返回结果:返回一个 Series,其索引与原始 DataFrame 的行索引(Index)一一对应。
df.mean(axis=1) # 计算每一行的平均值

复杂数据处理示例:去掉最高分与最低分

Section titled “复杂数据处理示例:去掉最高分与最低分”
  • 场景:在比赛评分中,通常需要去掉一个最高分和一个最低分后再计算平均值。
  • 实现逻辑
    • 编写自定义函数 trim_mean(data),利用 sum()max()min()len() 计算去极值后的平均值。
    • 难点:DataFrame 本身没有内置此方法,且不应直接修改第三方库代码。
def trim_mean(data):
data_len = len(data)
data_sum = data.sum()
max_num = data.max()
min_num = data.min()
return (data_sum - max_num - min_num) / (data_len - 2)

使用 apply() 方法扩展 DataFrame 功能

Section titled “使用 apply() 方法扩展 DataFrame 功能”
  • apply() 的作用:将自定义函数应用到 DataFrame 的每一行或每一列。
  • 执行机制
    • 当对 DataFrame 调用 apply() 时,它会将每一行或每一列作为 Series 传入自定义函数。
    • 配合 axis 参数,可以灵活控制函数作用的维度。

df.apply(trim_mean, axis=1)

- **逻辑优势**通过 `apply()`无需修改 Pandas 源码即可实现复杂的自定义统计逻辑保持了代码的模块化与 DRY (Don't Repeat Yourself) 原则
  • 默认行为axis=0(默认),每列数据依次传入函数
  • 示例trim_mean 应用于每列,进行纵向去最高/最低求平均
trim_mean(df, axis=0) # 每列作为参数,纵向统计
  • 每行数据作为函数参数,实现横向统计(如每学生平均分)。
trim_mean(df, axis=1) # 每行传入函数
  • 区别:函数应用于每一个元素,而非行/列。
  • 示例:加5函数 → 所有数字元素+5
df.applymap(lambda x: x + 5) # 每个元素独立处理
  • 返回新 DataFrame原 DataFrame 不变
  • apply():行/列级;applymap():元素级。
  • df.describe() 方法可一次性返回 DataFrame 中所有数值列的统计信息。
  • 包含指标
    • count:非空元素个数
    • mean:平均值
    • std:标准差
    • min / max:最小值与最大值
    • 25% / 50% / 75%:各分位数(50% 即中位数)
  • 智能忽略非数值列:若 DataFrame 中包含非数值类型(如字符串),describe() 会自动忽略这些列,仅对数值列进行统计计算。
  • 无需手动处理:无需预先提取数值列,直接对整个 DataFrame 调用即可,极大简化了数据探索流程。
  • 数据已准备好,两个 Series 通过字典创建 DataFrame。
  • 字典方式允许指定列名(如 student average)。
  • 创建后查看结果,即为期望的学生平均分 Series
  • 使用 max() 找每次考试最高分,输出第二高分 Series
  • 无内置函数获取第二高分
  • 建议结合匿名函数 (lambda) 创建,自行定义逻辑。
  • loc 提取 DataFrame 的分数列
  • apply() 应用函数到每列axis=0,默认值)。
  • 逻辑实现:由于 Pandas 没有直接获取“第二高值”的内置函数,需通过 apply() 配合 lambda 匿名函数自定义逻辑。

  • 操作步骤

    1. 使用 .loc 提取包含分数的子 DataFrame。
    2. 调用 .apply() 方法,默认 axis=0(纵向操作,即按列处理)。
    3. lambda 函数内部实现排序并取倒数第二位元素(或次大值)的逻辑。

示例逻辑:对每列数据排序后取倒数第二个值

Section titled “示例逻辑:对每列数据排序后取倒数第二个值”

df.apply(lambda x: x.sort_values().iloc[-2])

第二高分代码运行验证与等级函数引入

Section titled “第二高分代码运行验证与等级函数引入”
  • 运行代码后查看结果,对比原始分数确认输出为每列第二大值
  • 可手动检查准确性。
  • 删除测试代码。
  • 新需求:根据规则输出每个分数的等级
  • 先定义分数转等级函数
  • 复杂场景下,用具名函数(如 gethrate)替代匿名函数
  • 每个元素应用函数,使用 applymap() 方法传入函数名。
  • 注意:直接运行会因非数值列(如姓名)报错
def gethrate(score):
pass

df.applymap(gethrate)

  • 函数仅接受数字,非数字列传入会报错。
  • 先用 .iloc 提取分数相关列
  • 对分数列应用 applymap() 函数。
  • 运行代码后得到分数等级
df.iloc[...].applymap(gethrate)
  • 调用 describe() 方法。
  • Count:元素个数。
  • Unique:独特值(非重复值)。
  • 缺少:平均值、标准差、四分位数等完整统计信息。

Pandas describe() top/freq 与类型转换

Section titled “Pandas describe() top/freq 与类型转换”
  • top:最常见的值(众数)。
  • freq:最常见值出现的频次(频率)。
  • describe() 只对数字类型列展示统计信息。
  • 无法对字符串平均值标准差
  • 分数列类型为 object(宽泛类型,包括字符串、数字)。
  • Pandas 中字符串常用 object 类型表示。
  • 数字若为 object 类型,不会展示统计信息。
  • 需要将 object 类型分数列转换为数值类型,启用完整统计显示。
  • 非数字列自动忽略:仅在 DataFrame 既有数字列又有非数字列时忽略非数字列。
  • 纯 object 列情况:Pandas 认为无数字列,统一显示 object 类型信息(count、unique、top、freq)。
  • Series 方法S-type(int) 将 object 类型分数列转换为整数类型
  • 效果:转换后 describe() 显示完整统计(平均值、标准差等)。
  • 后续应用:数据清理环节常用此方法。
  • 优先API:网站提供时,直接获取结构化数据,更高效。
  • 备选爬虫:无API时,从网页提取数据。
  • 获取后存为csvjson等格式,便于后续读取修改。
  • 网上下载数据也多为不同格式。
  • 不同格式需不同读取方法,先了解格式再用代码读取。
  • 常见格式:txtmp3pdfzip等。
  • 后缀的作用:文件名后缀(如 .txt, .mp3)仅是文件名的一部分,用于提示操作系统调用对应的默认程序打开文件。
  • 修改后缀的后果:更改后缀名不会改变文件内部的实际数据格式或编码。如果将 .txt 改为 .mp3,系统会尝试用音乐播放器打开文本文件,导致无法正常读取。
  • 数据格式的定义:数据格式不仅指后缀,更核心的是文件内部内容所遵循的特定规则(如 JSON 格式遵循 JavaScript 对象表示法规则)。

JSON (JavaScript Object Notation) 数据格式

Section titled “JSON (JavaScript Object Notation) 数据格式”
  • 定义:全称为 JavaScript Object Notation,是一种轻量级的数据交换格式。
  • 应用场景:广泛用于存储和交换信息,特别是在通过 API 获取数据时,数据通常以 JSON 格式返回。
  • 优势:体积小,且能轻松被多种编程语言转换为其内部结构(如 Python 的字典和列表)。
  • JSON 对象:对应 Python 的字典(使用大括号 {})。
  • JSON 数组:对应 Python 的列表(使用中括号 [])。
  • 基本结构:使用大括号 {} 包裹,内部由键值对(Key-Value Pair)组成。
  • 键值对规则
    • 格式为 "键": 值
    • 键必须是字符串,使用双引号包裹。
    • 多个键值对之间使用逗号 , 分隔。
{
"id": "1",
"type": "article",
"title": "Working with JSON data",
"created": "2099-12-18T14:56:29.000Z"
}
  • 键的强制性规则
    • JSON 对象中的键(Key)必须是字符串,且必须使用双引号包裹。
    • 相比之下,Python 字典的键可以使用数字或不可变数据类型,且引号使用较灵活。
  • 嵌套规则
    • 该双引号规则不仅适用于顶层键,同样适用于嵌套在 JSON 对象或数组内部的所有键。
  • 数据类型限制
    • JSON 值仅支持以下几种类型:
      • 字符串(必须双引号)
      • 数字(整数或浮点数)
      • 布尔值(truefalse,注意 Python 为大写 True/False
      • 数组([]
      • 对象({}
      • 空值(null,对应 Python 的 None
  • 布尔值 (Boolean)
    • JSON 使用小写 truefalse
    • Python 使用首字母大写的 TrueFalse
  • 空值 (Null)
    • JSON 使用 null
    • Python 使用 None
  • 数据类型兼容性
    • JSON 的值支持:字符串(必须双引号)、数字(整数/浮点数)、布尔值、数组 ([])、对象 ({}) 和 null
  • 嵌套支持:JSON 结构支持无限嵌套,例如对象内可以包含数组,数组内也可以包含对象。
  • 数据交换优势:由于 JSON 的简洁性,它能被几乎所有主流编程语言轻松解析并转换为该语言内部对应的数据结构(如 Python 的列表或字典)。
  • 练习逻辑:通过判断给定的数据结构是否符合 JSON 规范(如键是否加双引号、布尔值大小写是否正确、是否存在重复键)来巩固理解。
  • 常见错误
    • 键名未加双引号。
    • 使用了单引号。
    • 键名重复(JSON 标准中对象键应唯一)。
  • 键未用双引号:学号、性别等键缺少双引号包围,必须是字符串形式。
  • 布尔值错误:‘已毕业’ 值使用大写,与JSON要求的小写 true/false 不符。
  • JSON 数组:以 [] 开头结尾。
  • 内容:三个 null 值,JSON 支持此类型。
  • 判断提示:需仔细查看细节才能确定是否有效。
  • 外层结构:中括号 [ 开头和 ] 结尾,为数组
  • 第一个值(对象)
    • 键使用双引号包围,为字符串。
    • 值1:数字(整数)。
    • 值2:内嵌数组,包含布尔值 true(小写)和整数 3。
  • 第二个值浮点数 3.14。
  • 第三个值数组,包含两个字符串
  • 最后一个值布尔值(有效类型)。

结论:所有结构和值类型符合JSON规范,为有效JSON。

  • 布尔值不匹配:JSON 使用小写 true/false,Python 使用大写 True/False
  • 直接使用问题:JSON 数据直接传入 Python 会报错,无法识别小写布尔值。
  • 解析步骤:需对 JSON 进行解析,才能转换为 Python 字典/列表进行分析。
  • 解析优势:步骤简单,使用 Pandas 操作丝滑便捷。
  • 体积小:轻量级数据交换格式。
  • 易转换:轻松转为编程语言内部结构(如 Python 的字典和列表)。
  • 使用 Pandas 读取和解析 JSON 数据,操作流畅,下节详解。
  • Pandas 提供了 pd.read_json() 函数,专门用于读取 JSON 文件并将其直接转换为 DataFrame。
  • 该函数自动完成文件读取、JSON 解析以及 DataFrame 构建的全流程,避免了手动编写复杂解析代码的需求。
import pandas as pd

survey_df = pd.read_json(“./cell_phones_survey.json”)

  • 原始 JSON 结构:通常是一个包含多个对象的 JSON 数组。
  • 映射规则:数组中的每一个 JSON 对象都会自动转换为 DataFrame 中的一行,对象的键(Key)对应列名(Column Name),值(Value)对应单元格数据。
  • 自动化处理:无需手动解析 JSON 格式中的嵌套结构或布尔值差异。
  • 效率提升:如果不用 Pandas,解析 JSON 每一行数据并将其转化为 DataFrame 格式可能需要编写多行代码,而 read_json 仅需一行即可完成。

JSON 对象与 DataFrame 的行映射机制

Section titled “JSON 对象与 DataFrame 的行映射机制”
  • 映射逻辑:JSON 数组中的每个对象自动对应 DataFrame 中的一行。
  • 结构对齐
    • JSON 对象中的 Key(键)自动转换为 DataFrame 的 列名 (Column Name)。
    • JSON 对象中的 Value(值)自动填充为对应单元格的 数据
  • 数据实例的一致性:这种结构天然契合 DataFrame 的设计理念,即每一行代表一个独立的数据实例,极大简化了从非结构化 JSON 到结构化表格的转换过程。
  • 非强制一致性:JSON 格式并不要求数组内所有对象拥有完全相同的键(Key)。
  • Pandas 的处理策略:当某些对象缺少特定的键时,Pandas 会自动在 DataFrame 中对应位置填入 NaN (Not a Number),表示数据缺失,无需手动进行繁琐的预处理。
  • read_json 的优势
    • 传统方式:手动解析 JSON 嵌套结构、处理布尔值大小写差异、手动构建字典列表。
    • Pandas 方式:仅需一行代码 pd.read_json() 即可完成读取、解析、类型对齐及 DataFrame 构建,极大地提升了数据处理效率。
  • JSON 键名 对应 DataFrame 的列名
  • 填充对应列的数据。
  • 当键值对的值为列表(如 Languages 长度为3)时:
    • Pandas 将列表每个元素视为独立数据实例
    • 展开为3行,不能仅占一行。
  • 表格要求每列行数一致
  • 其他属性值自动复制3份,填充对齐。
  • 结果:获得结构规则的表格

总结:已学会将JSON文件转换为DataFrame,下节处理实际数据。

  • JSON 结构易转换为编程语言原生结构。
  • Python 示例
    • JSON 对象 → 字典
    • JSON 数组 → 列表
    • Pandas 支持:JSON → DataFrame,一步到位后续分析。
  • CSV 是数据分析师最爱格式(非 JSON)。
  • 悬念:CSV 优势下期分解。
  • 纯文本本质:CSV 文件本质上是纯文本,不包含复杂的格式信息(如字体、颜色等)。
  • 表格化结构:虽然看起来杂乱,但通过逗号分隔,其结构就是一个标准的表格。
  • 数据对齐:当 CSV 数据对齐后,每一行代表一条记录,每一列代表一个属性,结构非常直观。
  • 全称:Comma-Separated Values(逗号分隔值)。
  • 设计逻辑:利用逗号作为字段间的分隔符,这种简单的设计使得 CSV 成为数据交换的通用格式。
  • 直接查看:无需手动处理分隔符,通过 Excel 等表格软件打开 CSV 文件,即可直接以表格形式展示数据。
  • 表头行:通常文件的第一行是表头(Header),用于定义每一列的名称(如 CustomerID, Gender, Age 等)。
  • 数据行:后续行即为实际数据,与表头列一一对应。
  • 灵活性:CSV 也可以没有表头,直接从数据行开始。
  • CSV DataFrame 行直接对应。
  • 每行数据包含的值数量必须相同,即逗号分割符数量一致。
  • 行数多或少,即为不合格无效的 CSV
  • 若值内含英文逗号,用引号包围整个值,避免误解析为分隔符。
  • 值含控制符,用两个逗号相邻表示中间内容存在。
  • CSV 为规整二维结构,一眼看出对应表格形状。
  • DataFrame 也是二维,与之契合;JSON结构灵活、多层嵌套,难以直观对应表格。
  • 使用代码编辑器文本编辑器按CSV规则编写。
  • 保存为**.csv**结尾文件。
  • CSV/JSON/TXT均为纯文本(重文本):无粗体、下划线、字号等格式。
  • 益处:减少文件体积,读取时避免格式干扰。
  • 非纯文本:Excel格式(含样式信息)。
  • 编程分析优先CSV/TXT等纯文本,或SQLite数据库。
  • Excel 可读取/修改/导出CSV。
  • 多数系统默认用Excel打开CSV文件。
  • 可在Excel编辑后导出为CSV。
  • Excel 的局限性:当 CSV 文件达到 GB 级别时,Excel 在加载数据、渲染格式及处理复杂功能时容易崩溃。
  • 代码编辑器方案:处理超大文件时,优先使用代码编辑器或纯文本编辑器打开,避免 Excel 的性能瓶颈。
  • Pandas 高效处理
    • 核心思路:将数据读取并转换为 DataFrame 后,利用 Pandas 的方法(如 head())仅查看部分数据,而非一次性加载所有内容。
    • 优势:按需读取,显著降低内存占用,避免系统卡顿。
  • 核心函数:使用 pd.read_csv() 函数直接读取 CSV 文件并转换为 DataFrame
  • 优势
    • 自动化处理:该函数自动处理文件读取、解析分隔符、构建 DataFrame 的全过程。
    • 内存优化:通过 head() 等方法仅加载并查看部分数据,避免一次性读取超大文件导致内存溢出或程序崩溃。
  • Excel 的局限
    • 面对 GB 级别的大文件,Excel 尝试渲染所有数据和格式会导致性能瓶颈,甚至程序崩溃。
    • 适合小规模数据编辑,不适合海量数据分析。
  • 代码方案的优势
    • 纯文本读取:Pandas 处理 CSV 时仅读取文本数据,无额外样式渲染开销。
    • 按需加载:通过代码可以精准控制读取的数据量,实现高效的数据探索。

pd.read_csv() 大文件实践与 header 参数

Section titled “pd.read_csv() 大文件实践与 header 参数”
  • 执行效率:传入 CSV 文件路径,几秒内将 16 万条数据文件转换为 DataFrame。
  • 结果验证:前几行显示每条数据一行,属性值对齐。
  • 无表头文件:若 CSV 第一行无表头(直接数据开始),设置 header=None
  • 列名规则:首行视为第一条数据,列名自动用 0 开始的数字代替。
  • 自定义索引 (Index):
    • 默认情况下 pd.read_csv() 使用从 0 开始的整数作为行索引。
    • 若 CSV 中某列(如 player_id)具有唯一标识属性,可在读取时指定 index_col 参数,将其设为索引。
pd.read_csv("./fifa_players.csv", index_col="player_id")
- **优势**:读取后可直接通过 `df.loc[id]` 快速定位球员数据,无需依赖位置索引。
  • 列显示限制:
    • Pandas 默认隐藏中间的列(用省略号表示),这在列数较多时会丢失信息。
    • 使用 pd.set_option 可以调整显示的列数上限。
pd.set_option('display.max_columns', 50) # 将显示上限提升至 50 列
  • 场景:当数据包含大量特征列且需要全览时,此设置能有效避免自动省略带来的信息缺失。
  • Pandas 默认隐藏中间的列(显示为省略号),可通过 pd.set_option 调整显示上限以查看所有特征列。
pd.set_option("display.max_columns", 150) # 设置显示列数上限为 150
  • 当单元格内容过长(如球员标签或特征描述)导致显示不全时,可调整 display.max_colwidth 参数。
  • 默认字符上限为 50,可根据需要增加,例如设置为 500。
pd.set_option("display.max_colwidth", 500)
  • 使用 head() 方法快速预览 DataFrame 的前几行数据。
  • 参数可传入整数以指定显示的行数,默认显示前 5 行。
  • df.info(): 查看 DataFrame 的整体概况,包括行数、列数、每列的数据类型以及内存占用情况。
    • 对于列数较多的 DataFrame,该方法会仅显示部分概况信息。
    • 如果数据量较小,该方法还会返回每列具体的数据类型及非空值的统计。
  • df.describe(): 针对数值型数据列进行统计分析,一次性输出多种统计信息。
    • 输出内容包括:计数 (count)、平均值 (mean)、标准差 (std)、最小值 (min)、25%/50%/75% 分位数以及最大值 (max)。
    • 仅适用于包含数值的列,对于非数值列,该方法会自动忽略。
  • 110个变量数据:Pandas 计算不到1秒完成。
  • Excel局限:无法瞬时处理,容易卡顿崩溃。
  • 代码优势:高效处理海量数据,远超Excel。
  • 数据获取后不立即分析
  • 先检查数据是否干净整洁
  • 如同买菜后检查泥土:先清洗泥垢虫子,再烹饪。
  • 数据清洗:去除脏数据,确保质量。
  • 结构:检查数据是否符合整洁标准。
  • 内容:后续评估数据值质量。
  • 乱数据:结构不符合规范的原始数据。
  • 整洁数据:符合埃德加·科德第三范式的三个特点:
    • 每列是一个变量(如贷款金额)。
    • 每行是一个观察值(如客户A的贷款)。
    • 每个单元格是一个
  • 每列:贷款相关变量。
  • 每行:一条贷款信息观察值。
  • 每个单元格:单一值,符合整洁数据特点。
  • 乱数据的特征
    • 一列包含多个变量(如姓名、年龄、地址混在一起)。
    • 一行包含多个观察值(如不同人的信息在同一行)。
    • 单元格包含多个值,导致数据难以直接分析。
  • 转换逻辑
    • 将乱数据拆解,使每一列仅代表单一变量。
    • 将原本横向排列的多个观察值转化为纵向的独立行。
  • 为什么需要转换
    • 整洁数据并非为了人类阅读方便,而是为了让代码能高效处理、分析和计算。
    • 很多直观的 Excel 表格在数据分析标准下属于“乱数据”,必须先进行重构。
  • 多变量列处理
    • 遇到列中包含多个变量(如“人数”和“年龄组”混合)时,需将其拆分为独立的列。
  • 整洁数据的核心价值
    • 统一格式后,计算机能更高效地进行聚合、筛选和统计计算。
    • 避免了因结构不规范导致的逻辑错误或复杂解析。
  • 结构与内容双重检查
    • 结构评估:是否符合整洁数据规范(第三范式)。
    • 内容评估:检查数据值本身是否存在缺失、异常或错误。
  • 数据清洗的必要性
    • 即使结构整洁,若数据值存在错误(如录入错误、异常值),分析结果仍会偏离事实。
    • 清洗过程是对原始数据进行纠错、补全或剔除,确保后续计算准确。
  • 数据缺失
    • 某些单元格为空值(NaN)。
    • 需评估缺失值对分析的影响,决定是填充、删除还是保留。
  • 数据重复
    • 同一观察值在数据集中出现多次。
    • 需根据唯一标识符(如学号、ID)进行去重,避免统计偏差。
  • 数据不一致
    • 同一变量在不同记录中格式或单位不统一(如日期格式混用、单位不一致)。
    • 需进行标准化处理,确保数据可比性。

数据内容质量评估:不一致数据

Section titled “数据内容质量评估:不一致数据”
  • 定义:同一变量在不同记录中格式、单位或含义不统一。
  • 常见场景
    • 单位不统一:例如身高数据有的以厘米为单位,有的以米为单位。
    • 格式混用:如日期格式(YYYY-MM-DD 与 DD/MM/YYYY)或数字格式(中文数字与阿拉伯数字混用)。
    • 全称与简称混用:例如“北京大学”与“北大”在同一列数据中并存。
  • 清洗策略:必须进行标准化处理,统一单位、格式或命名规范,确保数据具有可比性。
  • 无效数据:脱离现实规则的数据,例如人的身高为负数。
  • 错误数据:虽然符合格式规范,但数值逻辑不合理,例如成年人的体重记录为 3 公斤。
  • 手动评估的局限:当面对成千上万条数据时,肉眼检查效率极低,无法完成评估。
  • 代码化处理:通过编写代码(如 Pandas)进行自动化清洗,能够快速识别并处理缺失、重复、不一致以及无效错误数据,是高效数据分析的前提。
  • 提供列名数据类型等概况信息。
  • 快速了解结构:如列名销售额小华星2010包含销售额年份两个变量,不符合每列一个变量规则。
  • head():查看数据开头部分。
  • tail():查看数据结尾部分。
  • 通过部分数据评估整洁规则:每列一个变量、每行一个观察值、每个单元格一个值。
  • 参数传入行数,返回随机选出的行。
  • 每次运行结果不同,用于全面评估数据质量。
  • DataFrame 列数太多或值太长时,display() 展示不全。
  • 使用 set_option() 方法调整列数或值长度上限。
  • info() 方法列出每列非空值数量
  • 与总行数对比,即可判断该列是否存在空缺值
  • SeriesDataFrame 调用 isna()
  • 返回布尔 Series/DataFrame:空缺值为 True,非空为 False
  • 检查原数据中每个值是否为空缺。

使用 isnull() 与 sum() 评估缺失值数量

Section titled “使用 isnull() 与 sum() 评估缺失值数量”
  • isnull() 局限性:直接调用 isnull() 返回布尔值 DataFrame,对于大数据集难以直观判断缺失规模。
  • sum() 聚合统计:在 isnull() 后链式调用 sum(),可统计每列缺失值的具体数量。
    • 对 Series 调用:返回单个缺失值总数。
    • 对 DataFrame 调用:返回每列缺失值的 Series。
  • 原理:Python 将 True 视为 1,False 视为 0。sum() 通过对布尔值求和,将“是否存在缺失”转化为“缺失了多少个”。
  • True/False 的数值映射:在数学计算中,True 等同于 1,False 等同于 0。
    • 示例:True + 5 结果为 6,False + 5 结果为 5。
  • 应用价值:此特性使得利用 sum() 进行逻辑统计(如计算缺失值、统计满足条件的行数)变得非常高效。
  • 提取某列 Seriesdf['col']
  • 调用 isna() 返回布尔 Series,再调用 sum() 得到空缺值个数。
  • 直接 df.isna().sum()
    • 返回 Series,索引为列名,值为各列空缺值数量。
    • 因 DataFrame 列即 Series,逐列统计。
  • 使用条件筛选:df[df['col'].isna()]
  • 提取符合条件的行,查看空缺值详情。
  • 每列为 Series,可对特定列调用 isna() 方式。
  • Series 调用:返回布尔Series,检查当前值是否在前面出现过
    • 首次出现:False
    • 重复出现:True
  • DataFrame 调用:检查整行是否与前面某行完全相同
    • 仅当所有列值一致时标记为 True
  • 将条件放入 df[condition]:保留所有 True 对应行。
  • 示例:针对某列缺失值,提取包含空缺的行以深入分析。
  • 常见场景:姓名重复可能是同名,地址重复可能是室友,但姓名+地址同时重复才算数据重复。
  • 使用 duplicated(subset=[列1, 列2]):传入列列表,仅当指定多列同时重复时返回 True
  • 原理:布尔 Series 作为筛选条件,DataFrame 仅保留 True 对应行。
  • 难点:无法预测变种值(如拼写差异)。
  • 应对:Series.value_counts() 返回各值频次统计,帮助识别异常变种。
  • 核心问题:不同数据值可能代表同一目标(如单位不一致、全称与简称混用、中文与阿拉伯数字混用)。
  • 评估挑战:人工检查大型数据集不可行,需依赖代码自动化评估。
  • 无效数据:不符合逻辑规则的数据(如身高为负数)。
  • 错误数据:符合格式规则但事实不准的数据(如成年人体重为 3 公斤)。

使用 Series.sort_values() 发现异常值

Section titled “使用 Series.sort_values() 发现异常值”
  • 原理:通过排序将极小值或极大值排在首尾,便于直观发现离群点。
  • 应用:结合常识检查排序后的边界值是否合理。
  • 功能:快速展示数值列的最小值、最大值、均值等统计信息。
  • 价值:通过极值(min/max)快速判断是否存在异常数据,无需手动遍历。
  • 评估的目的是为了确定清洗的动作,遵循“有泥就冲水,有虫就摘虫”的原则。
  • 清洗不仅仅是确认脏乱程度,而是基于评估结果制定具体的修复计划。
  • 评估(Assessment)是发现问题的阶段。
  • 清洗(Cleaning)是解决问题的阶段。
  • 结构性问题(如变量混杂)通常需要进行数据重构(如宽表转长表)。
  • 内容性问题(如缺失、重复、异常)则需要针对性地删除、填充或修正。
  • 数据清洗技能随处理数据量的增加而变得熟练。
  • 随着处理经验的积累,对数据异常的敏感度会提升,评估与清洗的效率也会随之提高。
  • 处理内容前,先检查**列名(或行名)**是否有意义。
  • 若列名乱七八糟,需重命名重新排序,便于理解数据。
  • 一般顺序:先解决结构性问题,再处理内容性问题
  • 每列一个变量
  • 每行一个观察值
  • 每个单元格一个值
  • 不符合以上三点均为乱数据
  • 列=观察者、行=变量:需转置(行列对调)。
  • 一列含多个变量拆分列,将多变量分到其他列。
  • 有时拆分不够:需进一步调整结构。
  • 确保每列只包含一种变量
  • 确保每行是一个观察值:若一行为多个观察值,需拆分行让每个独立成行。
  • 有时光拆分不够,需重塑确保每行仅一观察值。
  • 宽数据(清理前)→ 长数据(清理后):为程序高效处理,而非人类直观。
  • 建议暂停审视清理前后例子差异。
  • 人工填充:当缺失值有明确的替代逻辑或真实值可考时,可手动填入。
  • 不处理:若缺失值不影响当前分析目标,可选择忽略。
  • 删除缺失行:若缺失的是关键变量,且该行数据对分析无贡献,直接删除该行。
  • 统计值填充:利用平均值、中位数或众数等统计指标替代空缺值,以保持数据规模。
  • 直接删除:通过 duplicated() 识别重复项后,直接删除重复行即可。
  • 保留策略:若不删除,需意识到重复数据可能导致分析结果偏差(如计数翻倍)。
  1. 人工填写缺失值
  2. 不处理缺失值
  3. 删除含缺失值的行
  4. 用统计值(如平均数)填充缺失值

针对不同数据和分析目的,清洗步骤存在差异,需根据实际情况灵活选择。

  • 标准化原则:统一同义表达,只保留一种方式,其余替换。
  • 删除异常值:如异常身高记录拉低平均值,直接删除该行。
  • Pandas NaN 特性:自动忽略空缺值,删除异常后平均值反而更准。
  • 替换异常值:用当前平均数等合理值替换。
  • 读取时 Pandas 可能将手机号误识为数值型,导致可求 max/min(不符合实际)。
  • 需手动转换类型,确保逻辑正确。
  • 调查表中**‘是/否’字符串替换为True/False**。
  • 数据本身无问题,但布尔型便于后续分析逻辑判断
  • 拆分列删除重复行处理缺失值数据类型转换
  • 数据列名乱七八糟时,用**DataFrame.rename()**方法重命名。
  • 功能:通过传入字典参数修改 DataFrame 的索引 (index) 或列名 (columns)。
  • 语法

df1.rename(index={old_name: new_name})

df1.rename(columns={old_name: new_name})

- **机制**字典的键 (key) 为原名称 (value) 为新名称
- **注意事项**
- `rename()` 方法默认返回一个新的 DataFrame**不会**修改原始数据
- 若需原地修改可使用 `inplace=True` 参数或将结果重新赋值给原变量 `df1 = df1.rename(...)`)。
  • inplace=True 优化:设置 inplace=True 时,直接修改原 DataFrame,无需重新赋值;方法不返回新对象,避免复制开销。
  • 字典重命名局限:适合少数列名修改;批量处理几十上百列时,手动编写字典过于繁琐。
  • 函数重命名优势:传入针对 Series 的函数(Pandas 内置或自定义),原列名作为参数,函数返回值即新列名,实现高效批量处理。
  • 核心优势:当需要批量修改数十或上百个列名时,手动编写字典过于繁琐,利用函数进行批量转换更高效。
  • 实现逻辑
    • 将现有的索引或列名作为参数传入指定的函数(如 Pandas 内置字符串方法或自定义函数)。
    • 函数的返回值将自动作为新的索引或列名。
  • 示例应用

使用 str.upper 将所有列名批量转换为大写

Section titled “使用 str.upper 将所有列名批量转换为大写”
df.rename(columns=str.upper, inplace=True)
- **逻辑依据**利用函数映射机制将列名处理逻辑从手动定义映射表转变为定义转换规则”,极大提升了大规模数据清洗的自动化程度
  • 默认行为:不指定 axis 时,默认 axis=0,沿着**行(索引)**纵向排序。
  • 指定&;axis=1:按列名排序。
  • 关键特性(与 rename() 类似):
    • 默认返回新 DataFrame,不修改原始数据。
    • 使用 inplace=True:直接修改原 DataFrame,不返回新对象(无需赋值返回结果)。
  • set_index():将 DataFrame 中的某一列数据设置为索引,返回一个新的 DataFrame。
  • reset_index():与 set_index() 相反,将当前的索引重置为默认的整数序列,并将原索引作为一列数据添加回 DataFrame。
  • sort_index():对索引或列名进行排序。
    • 默认行为axis=0(默认),按行索引(index)进行纵向排序。
    • 列名排序:设置 axis=1,按列名(columns)进行横向排序。
    • 原地修改:与 rename() 一致,默认返回新对象。若需直接修改原 DataFrame,需设置 inplace=True,此时方法不返回任何值。
  • 结构优先原则:在处理数据内容(脏数据)之前,必须先修复结构性问题(如行列转置、列名重命名、索引设置)。
  • 整洁数据 (Tidy Data) 定义
    • 每列是一个变量。
    • 每行是一个观察值。
    • 每个单元格是一个值。
  • 宽数据 vs 长数据:清洗过程常涉及将“宽数据”转换为“长数据”以利于程序处理,即便有时宽数据对人类阅读更直观。
  • 缺失值处理策略
    • 忽略:Pandas 计算时自动跳过缺失值,适用于非关键变量。
    • 删除:若关键变量缺失,直接移除该行。
    • 填充:使用平均值、中位数或众数填补空缺。
  • 不一致与异常数据处理
    • 标准化:将多种表达方式统一为一种(如将“是/否”字符串转换为 True/False 布尔值,便于逻辑判断)。
    • 异常处理:对于严重偏离的异常值(如身高记录错误),可选择删除或替换,防止其拉低整体统计指标。
  • 适用场景:当某一列包含多个变量(例如“人口密度”列包含总人口和面积)时,需要将其拆分为独立的列。
  • 基本语法

df[‘column’].str.split(‘/’)

- **返回结果**默认返回一个包含列表的 Series每个元素都是拆分后的子字符串列表
  • 问题:如果直接使用 str.split(),结果仍为 Series,无法直接作为 DataFrame 的多列存在。
  • 解决方案:设置 expand=True 参数。

将拆分后的结果直接扩展为 DataFrame 的多列

Section titled “将拆分后的结果直接扩展为 DataFrame 的多列”

df[‘column’].str.split(‘/’, expand=True)

- **效果**将拆分后的数据直接转化为一个新的 DataFrame每一部分对应一列便于后续分析
  • 结构重塑:在处理内容错误前,必须先完成列名重命名、索引设置、数据转置及多变量列的拆分。
  • 整洁数据 (Tidy Data) 标准:确保每列仅代表一个变量,每行代表一个观察值,每个单元格仅包含一个值,这是后续高效计算的前提。

DataFrame 拆分列合并、删除与拼接实践

Section titled “DataFrame 拆分列合并、删除与拼接实践”
  • 将拆分后的新DataFrame合并进原DataFrame:
df'col1', 'col2' = new_df # new_df 包含对应列
  • 列名存在时:更新列数据。
  • 列名不存在时:添加新列。
  • 拆分后原列仍保留,使用drop删除:
df.drop('original_col', axis=1)
  • 完成结构性问题清洗。
  • Series的**.str.cat()方法拼接另一个Series**。
  • 后续:重复添加列 + 删除多余列操作。
  • 核心需求:将“宽数据”(多列表示同一类变量的不同取值)转换为“长数据”(将列名转换为变量值,便于分析和绘图)。
  • 方法pd.melt(df, ...)
  • 关键参数
    • id_vars:指定不需要转换、需保留的“标识列”(如:国家代码、年份)。
    • var_name:转换后,原列名所在的新列的名称(例如:“年龄组”)。
    • value_name:转换后,原单元格数值所在的新列的名称(例如:“肺结核病例数”)。
  • 转换逻辑
    • pd.melt 将除 id_vars 之外的所有列名作为“变量”放入一列,并将对应的数值放入另一列。
    • 这种重塑使得数据更符合“整洁数据”原则,便于后续基于特定变量(如年龄组)进行分组聚合计算。
  • 核心需求:将“宽数据”(多列表示同一类变量的不同取值)转换为“长数据”(将列名转换为变量值),以便于后续分组聚合分析。
  • 转换逻辑pd.melt 将除 id_vars 之外的所有列名作为“变量”放入一列,并将对应的数值放入另一列。
  • 关键参数
    • id_vars:指定不需要转换、需保留的“标识列”(如:国家代码、年份)。
    • var_name:转换后,原列名所在的新列的名称(例如:“年龄组”)。
    • value_name:转换后,原单元格数值所在的新列的名称(例如:“肺结核病例数”)。
  • 数据特性melt 不改变原始 DataFrame,如需保存结果需重新赋值。
  • 适用场景:当 DataFrame 的某一列值是“列表”而非单一值时,需要将列表中的每个元素拆分为独立的一行。
  • 实现方法:使用 df.explode('列名')
  • 优势:将原本包含多个课程的列表行,拆解为多个单行记录,便于针对“课程”进行统计(如计算某课程被选择的次数)。
  • 基本用法df.drop(labels, axis=...)
  • 参数说明
    • axis=0(默认):删除行。
    • axis=1:删除列。
  • 特性:与 rename() 一致,默认返回新对象,不修改原始 DataFrame;若需原地修改,需设置 inplace=True
  • 整列统一填充:针对整列缺失值,直接赋值统一值。
df['列名'] = 统一值

所有元素替换为指定值。

  • 特定位置填充:用 .loc[].iloc[] 定位后赋值。
    • .loc 语法.loc[行标签, 列名] = 新值
    • 示例:单个缺失值替换、指定位置填充。
  • 切片或部分替换:.loc/.iloc 提取的部分均可通过赋值填充或替换。
    • 适用于单值、范围或条件选区。
  • 大缺失场景:需自行定位位置,手动处理效率低。
  • Series.fillna():自动寻找所有 NaN 值并根据指定参数进行填充,无需手动逐个定位。
    • 常量填充df['B'].fillna(0) 将列中所有 NaN 替换为 0。
    • 计算填充:可传入计算结果(如平均值),例如 df['B'].fillna(df['B'].mean())
  • DataFrame.fillna():支持对整个 DataFrame 进行全局填充,所有缺失值会被统一替换为传入的参数。
  • 字典映射填充:支持传入字典,实现按列指定不同的填充值(例如 df.fillna({'A': 0, 'B': 10}))。
  • 默认行为:与 rename()drop() 一致,fillna() 默认返回填充后的新 DataFrame(或 Series),不会修改原始数据。
  • 原地修改:若需直接在原 DataFrame 上进行填充,可设置 inplace=True 参数。
df4 = df4.fillna(0) # 重新赋值方式
df4.fillna(0, inplace=True) # 原地修改方式
  • 填充 (fillna):适用于缺失值可以用特定值(如 0)或统计值(如平均值)合理替代的情况。
  • 删除 (dropna):当缺失值出现在对分析目标至关重要的关键数据列中,且无法通过填充进行合理推断时,必须选择丢弃这些包含缺失值的观测值。
  • 核心功能:用于删除包含缺失值(NaN)的行或列。
  • 默认行为:调用 df.dropna() 时,默认删除所有包含至少一个 NaN 值的行。
  • 适用场景:在清理数据时,如果某一行中任何一个关键指标缺失,调用此方法可以确保后续分析基于完整数据进行。
  • 局限性:若数据清理更关注特定列的完整性,直接使用 dropna() 可能会误删包含其他非关键列缺失值的行,需结合具体列名进行筛选。

使用 subset 参数精细化删除缺失值

Section titled “使用 subset 参数精细化删除缺失值”
  • 问题:直接调用 dropna() 会删除包含至少一个 NaN 的整行,这可能导致非关键列的缺失误删了关键数据。
  • 解决方案:使用 subset 参数指定需要检查缺失值的列。
df.dropna(subset=['工资'])
- **逻辑**:仅在 `subset` 指定的列中检查缺失值。如果这些关键列存在 NaN,则删除该行;若非关键列存在 NaN,则保留该行。
  • 默认行为axis=0,沿着行索引操作,删除包含缺失值的行。
  • 列操作:设置 axis=1,沿着列名操作,删除包含缺失值的列。
    • 应用场景:当某列大部分数据缺失且不具备分析价值时,可直接按列删除。
  • drop_duplicates():删除 Series 中的重复值或 DataFrame 中的重复行。
  • 默认行为:不传参数时,只有整行所有变量完全相同才视为重复并删除。
  • 语法df.drop_duplicates(subset=['列名1', '列名2'])
  • 逻辑:仅检查 subset 列表中的列,若这些列值重复(即使其他列不同),则删除重复行。
  • 示例:行 0 和行 4 的年龄不同,但若 subset=['姓名', '性别'] 相同,则视为重复删除。
  • 特性:类似 dropna(),默认返回新 DataFrame,需 inplace=True 修改原数据。

drop_duplicates() keep 参数与删除规则

Section titled “drop_duplicates() keep 参数与删除规则”
  • 默认删除逻辑:相同行出现多次时,首次出现保留,后续重复删除
  • keep 参数控制
    • 默认 keep='first':删除第二遍及之后。
    • keep='last'保留最后一次,删除之前重复。
  • 不修改原数据:需重新赋值或设置 inplace=True
  • 通用用法:适用于 SeriesDataFrame,用法和效果相似。
  • 单个值替换:传入两个值(如数字或字符串),将前者全部替换为后者。
df.replace(旧值, 新值)
  • 列表批量替换:第一个参数为列表,所有列表值替换为单一新值,实现同一替换。
df.replace([旧值1, 旧值2], 新值)
  • 字典映射替换:传入字典,指定不同旧值对应不同新值。
df.replace({旧值1: 新值1, 旧值2: 新值2})
  • 默认行为:不修改原数据,返回新对象,需重新赋值。
  • 原地修改:设置 inplace=True
df.replace(旧值, 新值, inplace=True)
  • 必要性:某些方法(如求平均值)仅适用于特定类型,不能对字符串求平均。
  • astype() 方法:传入目标类型,如 intfloatstrbool
s.astype(float)
  • 返回对象的类型名称
  • 忘记类型拼写时,可通过 type() 查看。
  • Python 字符串在 Pandas 中显示为 object 类型。
  • 使用 astype(str) 转换后,结果为 object
  • 分类数据:包含有限数量不同类别的数据。
  • 示例:出生性别(仅两种类型)。
  • 数据分为分类数据数值数据
  • 存储优势:将具有有限类别的列(如性别、部门、国家)转换为 category 类型,可以显著节省内存空间。
  • 分析优势:在后续进行统计分析或可视化时,Pandas 会自动选择最合适的统计方法或图表类型。
  • 转换方法:使用 astype() 方法进行转换。

s1.astype(“category”)

  • 非内置类型category 是 Pandas 库特有的类型,而非 Python 内置类型。
  • 字符串引用:在 astype() 中调用时,必须使用引号将其作为字符串传入(例如 "category"),否则会报错。
  • 数值数据 (Numerical Data):指通过测量得到的观测值,具有具体的数值,支持求和、求平均值等数学运算(例如身高、人数)。
  • 分类数据 (Categorical Data):指种类有限的数据,通常用于描述属性(例如性别、颜色、公司部门)。
  • 好处:清洗后保存干净版本,下次分析直接读取,避免重复评估和清洗步骤,节省时间。
  • 本质:将 DataFrame 写入新建空白文件,对应 pd.read_csv() 的写入方法。
  • 方法to_csv(),传入文件路径参数。
df.to_csv('cleaned_file.csv')
  • 应用场景:完成清洗后立即保存,支持后续多种分析方式。
  • index 参数to_csv() 默认会将 DataFrame 的索引(Index)一并保存到 CSV 文件中。如果不希望保存索引,可设置 index=False
  • header 参数:默认保存列名。若不需要列名,可设置 header=False
df.to_csv("cleaned_sales_data.csv", index=False)
  • 索引偏移:若保存时包含索引(默认),读取时 Pandas 会自动将索引列识别为数据列(通常命名为 Unnamed: 0),导致列数增加。
  • 解决方案:在读取时使用 index_col 参数指定哪一列作为索引,或在保存时明确设置 index=False 以避免生成多余索引列。
  • 有意义索引恢复:读取CSV时若索引列被误认数据列,用rename()为其换有意义名字,再用set_index()设为DataFrame索引。
  • 写入时忽略索引to_csv(index=False)自动忽略索引写入,仅保存数据内容。
  • 默认索引优势:下次读取无多余索引列(Unnamed: 0),无需额外处理。
  • 有意义索引风险:若原索引含关键信息,index=False会导致丢失;默认从0开始的索引则无此问题。
  • 经过数据评估原则与清洗方法学习后,上手实践评估和清理真实数据集
  • 实际项目:复习前面知识,通过动手深刻掌握数据相关技能
  • 除了整理好的数据集,前视频提到的数据社区链接在文档中。
  • 包括百度旗下平台阿里旗下平台数据科学相关社区
  • 探索更多有趣数据,可在不同平台查找。
  • 下一节:开启第一个实际项目(时辰项目)。
  • 项目回顾:通过实战复习数据评估与清洗流程,综合运用之前技巧。
  • 报告表达重要性:数据分析中需清晰表达逻辑与思路,关注报告结构与表达
  • .ipynb分享不便:文本编辑器打开内容不直观不可读;需用Jupyter Notebook或代码编辑器打开。
  • 分享解决方案:上传到D号,便于他人查看。
  • 仅支持字母、数字、短横杠(-)
  • 输入中文不行
  • 是否接受宣传邮件:输入 Y(想)或 N(不想)。
  • 真人验证:点击验证按钮,旋转动物图片,点击提交。
  • 点击 create account
  • 邮箱收到验证码,在页面输入完成注册。
  • 登录 github.com 主页。
  • 创建仓库:点击 new 按钮。
  • 仓库概念:相当于项目文件夹,所有项目相关文件存放在仓库下。
  • 文件不能直接上传,必须在仓库之下。
  • 仓库名:仅字母、数字、英文句号下滑线短线,如 Python Data Analysis
  • Description:简短说明仓库内容,可用中文。
  • Public/PrivatePublic 分享可见,Private 仅自己。
  • Add a README file:提供项目介绍(.md Markdown 文件,使用前面学过的语法)。
  • README.md 的作用:作为项目的说明文档,支持 Markdown 语法,用于向访问者介绍项目背景、功能及使用方法。
  • 初始化选项
    • Add .gitignore:可暂时选择忽略(None),后续根据需要添加。
    • Choose a license:用于规定代码的使用权限(如是否允许商用、是否必须署名等)。
  • License 资源推荐:若不确定选择哪种协议,可访问 choosealicense.com 查看不同开源协议的含义与适用场景。
  • 仓库创建完成:配置完成后点击 Create repository,即可生成包含 README 文件的初始代码仓库,后续可直接在此基础上进行代码上传与管理。
  • 上传 Notebook 文件
    • 在仓库主页点击 Add file -> Upload files
    • .ipynb 文件直接拖拽进上传区域。
    • 支持一次性上传多个文件,包括 CSV 数据文件。
  • 提交记录 (Commit)
    • 必须填写提交说明(Commit message),用于记录本次变更的内容。
    • 提交后,GitHub 会自动渲染 .ipynb 文件,使其在网页端即可直接阅读,无需本地环境。
  • 文件查看与交互
    • 点击仓库中的 Notebook 文件,GitHub 会将其渲染为可读的静态页面,保留了单元格代码与输出结果的排版。
  • 文件删除操作
    • 进入具体文件,通过界面提供的删除选项(通常在文件预览页面的菜单中)即可移除不再需要的文件。
  • Markdown 实时预览
    • 在 GitHub 编辑 README.md 时,可以使用 Edit 模式配合 Preview 标签页,实时查看 Markdown 渲染效果,确保项目说明文档格式正确。

GitHub 文件删除与 Markdown 实时预览

Section titled “GitHub 文件删除与 Markdown 实时预览”
  • 文件删除操作
    • 进入需要删除的文件页面。
    • 点击右上角的“三点”菜单(通常包含 Delete file 选项)。
    • 注意:删除操作仅在点击 Commit changes 按钮后才会正式生效,这体现了 Git 的版本控制逻辑。
  • Markdown 实时预览
    • 在编辑 README.md 等 Markdown 文件时,GitHub 提供 EditPreview 标签页。
    • 通过在 Edit 模式下修改内容并切换至 Preview 标签,可实时查看渲染效果,确保文档排版符合预期。
  • 上传报告后获取GitHub链接
  • 将链接嵌入电子简历文字中。
  • 查看者点击文字直接跳转项目,查看具体内容。
  • 操作便捷。
  • join是方法,需在DataFrame上调用,非独立函数。
  • 默认多列匹配(如客户ID等所有列)。
  • 参数指定合并类型:left, right, inner, outer

DataFrame join 重名列错误与后缀处理

Section titled “DataFrame join 重名列错误与后缀处理”
  • 重名列报错:CustomerDS4 和 OrderDS4 共享 CocoID 列,join 保留两表所有列导致冲突,直接报错。
  • 后缀参数解决
    • lsuffix:指定左表(CustomerDS4)后缀。
    • rsuffix:指定右表(OrderDS4)后缀。
  • 区分作用:结果中明确标识列来源,避免混淆。
  • 与 merge 差异:join 不自动添加默认后缀,必须手动指定,否则报错。
  • 已学合并方法
    • concat:用于拼接(军阶)。
    • merge:根据列值合并。
    • join:根据所有列合并。
  • 随着数据表增多,merge 使用频率增加,常用于复杂场景如数据不查
  • 数据包含:超市分店不同时间段商品类别销售额销售数量
  • 数据本身整洁
  • 常见需求:查看不同分店的销售情况。
  • 分属区(pivot)和操作在数据分析中非常常见
  • 下一节学习更多数据重塑方法,提升 DataFrame 操作灵活性。
  • 问题:同一分店数据分散在多行,难以分析。
  • 解决方案:使用 df.groupby('分电编号'),自动将相同值的行组合在一起。
    • 示例:所有001编号行聚合成组,所有002行聚合成组。
df.groupby('分电编号')
  • 结果:得到 DataFrameGroupBy 实例,不是 DataFrame。
  • 原因:分组后每个组有多个数据,不是有效表格,无法直接查看结构。
  • 定义:指能够对一组数据进行汇总计算的函数,将多个数据点转化为单一结果。
  • 常见聚合函数
    • 计数:count()
    • 求和:sum()
    • 平均值:mean()
    • 最大/最小值:max(), min()
    • 中位数:median()
    • 标准差/方差:std(), var()
    • 首尾元素:first(), last()
  • 应用场景:在 groupby 分组后,通过调用聚合函数对各组数据进行统计。
  • 操作流程:先通过 groupby 对数据进行分组,再调用聚合函数计算。
  • 代码示例

df.groupby(‘分店编号’)[‘销售额’].mean()

- **逻辑**`groupby` 生成 `DataFrameGroupBy` 实例调用聚合函数后Pandas 会自动对每一组内的数据应用计算逻辑并返回一个包含统计结果的 `Series` `DataFrame`
  • 分组后的状态groupby() 返回的 DataFrameGroupBy 实例仅是逻辑上的分组,并不直接呈现为表格,必须配合聚合函数(如 .mean())才能得到最终的 SeriesDataFrame 结果。
  • 聚合结果的维度
    • 若聚合前指定了单个变量,结果为 Series
    • 若聚合前指定了多个变量,结果为 DataFrame
  • 索引规则:无论结果类型如何,分组变量(如“分店编号”)会自动成为结果的索引。
  • 可以在 groupby() 中传入一个列表,实现基于多个变量的分组。

示例:同时对分店编号、销售额和销售数量进行分组聚合

Section titled “示例:同时对分店编号、销售额和销售数量进行分组聚合”

df.groupby([‘分店编号’])’销售额’, ‘销售数量’.mean()

- **逻辑要点**分组的完整逻辑是分组 -> 聚合”,中间的 `DataFrameGroupBy` 状态仅用于定义分组规则不包含实际的数据表格结构
  • 核心逻辑:除了内置的 mean()sum() 等聚合函数,还可以通过 apply() 方法传入自定义函数,实现更复杂的统计需求。
  • 实现要求:自定义函数必须满足将 Series(一组数据)转换为单一数值的逻辑。
def max_plus_10(nums):
return nums.max() + 10

在分组后使用 apply 调用自定义函数

Section titled “在分组后使用 apply 调用自定义函数”

df.groupby(‘分店编号’)[‘销售额’].apply(max_plus_10)

- **适用场景**当内置聚合函数无法直接满足业务逻辑如本例中需在最大值基础上加 10自定义函数提供了极高的灵活性
- **注意点**`apply` 能够处理 `groupby` 生成的每个组将该组的 `Series` 作为参数传递给自定义函数并返回计算后的结果
  • pivot_table:Pandas 的聚合函数,中文为透视表,类似于 Excel 透视表功能。
  • 作用:基于原始数据重塑表格结构并进行聚合运算,提升数据分析灵活性。
  • 场景:展示生鲜休闲食品不同分店不同时间段的销售情况。
  • 需求:直观查看各类商品在各分店、各时间段的销售总额
  • 实现参数
    • index=‘分店编号’(第一层索引)
    • columns=‘时间段’(第二层索引)
    • values=‘销售额’(求和值)
    • aggfunc=‘sum’(聚合操作:求和)
  • 效果:改变原始表结构,生成汇总透视表,便于分析。
  • 函数调用:传入 indexcolumnsvaluesrkfok 参数。
  • index:列表 ['分店编号', '时间段']
  • columns'商品类别'
  • values'balance'
  • rkfoknumpide.sum(求总额)。
ppppp(
index=['分店编号', '时间段'],
columns='商品类别',
values='balance',
rkfok=numpide.sum
)
  • 原因Series 底层实现就是 numpide 数组,所以针对数组的函数也可以用在 Series 上。

pivot_table 数据传递与默认 aggfunc 实践

Section titled “pivot_table 数据传递与默认 aggfunc 实践”
  • 关键错误:忘记传入 df,代码无法指定操作数据,必须传入 df.pivot_table(...)
  • 默认行为:不传入 aggfunc 时,默认使用 mean()(求平均),运行正常生成表,无报错。
  • 验证方式:运行无 aggfunc 代码,观察表结构与行/列匹配设定。
  • 实验建议:调整 indexcolumnsvaluesaggfunc 参数,重塑表结构。
  • 变换示例index='商品类别'columns='分店编号',继续计算聚合。
  • 功能重叠:两者均可用于分组聚合运算。
  • 差异点
    • pivot_table:除分组外,支持将特定变量作为列 (columns) 进行重塑,生成更直观的二维透视表,便于阅读和理解。
    • groupby:分组后,分组变量只能作为索引 (index),无法直接重塑为列,主要用于纯粹的聚合计算。
  • 应用场景:当目标不仅是聚合,还需要改变表格结构以提升数据展示直观性时,首选 pivot_table
  • 变量选择逻辑:想分析地区差异时,用地区变量分组聚合;想分析年龄差异时,用年龄变量分组聚合,直观直接。
  • 初学者理解建议:分组聚合概念需时间消化,未完全理解可多看几遍或用 DataFrame 多操作实践。
  • 核心价值:数据分析必备方法,根据分析切入点灵活选择分组变量。
  • pd.cut() 调用:传入 df1年龄 Series + 已创建的分箱标准(bins)。
  • 返回值:新的 Series,数据类型为 category(表示有限种类,如年龄范围、性别、公司部门)。
  • category 优势:比字符串类型更节约内存空间。
  • 结果依据:根据传入 bins 的划分生成区间类别。
  • 分箱目的:解决连续数值(如年龄)因取值过多导致分组后类别过于细碎的问题。

  • 实现逻辑

    1. 定义分箱边界列表(如 age_bins = [0, 10, 20, 30, 40, 50, 60, 120])。
    2. 使用 pd.cut(series, bins) 将数值映射到预定义的区间中。
  • 返回值特性

    • 返回一个 category 类型的 Series
    • 内存优势:相较于字符串(object)类型,category 类型在处理有限种类数据(如年龄段、性别、部门)时能显著节省内存。
  • 圆括号&;():表示不包含该端点值(开区间)。
  • 方括号&;[]:表示包含该端点值(闭区间)。
  • 示例(40, 50] 表示大于 40 且小于等于 50 的范围。
  • 需求:默认的区间表示(如 (40, 50])较为数学化,不够直观。
  • 解决方案:通过 labels 参数传入自定义名称列表(如 ['儿童', '少年', '青年', ...])。
  • 实现代码
age_labels = ['儿童', '少年', '青年', '中年', '中老年', '老年']
pd.cut(df['年龄'], bins=age_bins, labels=age_labels)
  • 优势:将抽象的数字区间转换为具有实际业务含义的分类标签,便于后续分组统计。

pd.cut() labels 执行结果与 DataFrame 列添加

Section titled “pd.cut() labels 执行结果与 DataFrame 列添加”
  • 结果验证pd.cut() 返回 category 类型 Series,值替换为指定 labels(如 age_labels)。
  • 标签对应规则
    • 第一个标签 '儿童' → 第一个区间 (0, 10]
    • 第二个标签 '青少年' → 第二个区间 (10, 20]
    • 依此类推,按 bins 顺序一一映射。
  • 分组应用:添加为 DataFrame 新列(如 '年龄段'),后续按年龄范围轻松分组聚合分析。

分组聚合的层级索引 (Hierarchical Indexing)

Section titled “分组聚合的层级索引 (Hierarchical Indexing)”
  • 多变量分组现象:当 groupby() 使用多个变量(如 ['分店编号', '时间段'])进行分组时,生成的 DataFrame 会自动产生层级索引
  • 层级索引提取
    • 依然可以使用方括号 [] 配合标签索引来提取特定行。
    • 使用 .loc 访问时需注意层级结构,例如 grouped_df.loc['001'] 可以提取分店 001 的所有时间段数据。
  • 外层索引与切片:当使用 .loc 对外层索引进行切片(如 grouped_df.loc['001':'002'])时,可以一次性获取多个分店的数据,操作逻辑与普通 DataFrame 一致。
  • 内层索引提取:对多层级结果使用内层索引继续提取特定行,如 grouped_df.loc['001', '上午'] 获取单行数据。
  • reset_index() 方法:重置层级索引为普通整数索引(从0开始),原层级索引转为新列数据。
  • 效果:消除层次化结构,所有索引值变为独立列,便于后续操作。
  • query() 功能:获取符合条件的行组成的 DataFrame
  • 与布尔索引等价:结果同将 布尔 Series 传入 df[] 筛选 True 行,但写法更简洁直观。
  • 调用语法df.query('字符串条件')queryDataFrame 方法,传入字符串表达式。
  • 示例:用字符串替换复杂布尔条件,直接过滤数据。
  • 列名引用:在 query() 的字符串表达式中,列名直接使用,无需引号包围,也无需通过 df['列名'] 引用。
  • 字符串值处理
    • query() 外部使用单引号,内部字符串值应使用双引号(反之亦然),以避免引号嵌套冲突。
    • 若内外引号类型必须一致,可在内部引号前使用反斜杠 `` 进行转义(例如 `‘性别 == “男”’’性别 == \‘男\’’`)。
  • 逻辑连接query() 支持直接使用 andor 关键字进行多条件逻辑连接,相比布尔索引的 &| 符号,语义更接近自然语言。
  • 项目目标:使用真实 Netflix美剧数据集 进行数据整理,复习知识并掌握数据技能。
  • 数据集内容:包含 两个数据文件(用于练习数据连接)+ 数据集介绍的 Jupyter Notebook 文件。
  • 准备步骤
    • 下载数据集和 Notebook 文件。
    • 创建 单独文件夹 保持文件结构整洁,将所有文件放入同一目录。
  • 启动实践:打开并运行 Jupyter Notebook,开始实际数据整理。
  • 分析任务
    • 流派整理影视作品(喜剧片动作片纪录片等)。
    • 计算各流派中不同演员出演作品的平均IMDb评分
    • 找出各流派评分最高的演员。
  • IMDb 解释:国外版豆瓣,用户为影视作品打分。
  • 应用场景:导演选角,如查看喜剧片平均评分最高的演员。
  • 数据集特点:聚焦Netflix电视剧与电影(相当于国外腾讯视频/爱奇艺),包含两个数据文件
  • titles.csv:包含影视作品核心信息(ID、标题、类型、描述、流派、IMDb评分等)。
  • credits.csv:包含演职人员信息(姓名、角色、所属作品ID)。
  • 关联机制:两表通过“影视作品ID”进行关联,确保能将演员信息与具体作品及其评分对应起来。
  • 核心任务
    • 按流派(喜剧、动作、纪录片等)归类作品。
    • 计算各流派中演员的平均 IMDb 评分。
    • 识别各流派中评分最高的演员。
  • 分析价值
    • 选角决策:导演可通过数据分析,筛选特定流派中平均评分最高的演员,优化选角策略。
    • 背景知识:IMDb 相当于国外版“豆瓣”,Netflix 相当于国外版“腾讯视频/爱奇艺”。
  • 数据清洗原则
    • 始终结合分析目标进行清洗,目标决定了处理缺失值或异常值的具体策略(如:删除无效行 vs. 填充数据)。
    • 结构化问题(如列名、数据格式)应优先于内容质量问题进行处理。
  • 逻辑转换:数据清洗不仅仅是技术操作,更是将原始数据转换为“整洁数据 (Tidy Data)”以支持后续分析的逻辑过程。
  • 核心变量:作品所属流派演员、演员参演作品的 IMB评分
  • 下一步流程:阅读数据每列变量含义 → 读取数据 → 评估数据 → 清理数据 → 整理数据。
  • 演示简化:已预先完成读取、评估、清理步骤,避免演示冗长。
  • 评估+清洗方式:同步进行(发现问题立即清理),比上个项目分开更省事;具体操作见课程配套文件。

Netflix 数据清洗实战:genres 列拆分

Section titled “Netflix 数据清洗实战:genres 列拆分”
  • 问题背景:原始数据中的 genres 列包含多个流派(如 ['drama', 'crime']),直接进行分组统计会导致数据混乱,无法精确分析单一流派的特征。
  • 清洗思路
    • 目标是让每个流派在各自的行中独立存在,实现“一行一流派”。
    • 使用 str.split() 将列表字符串拆分,配合 apply(pd.Series) 将拆分结果扩展为多列,最后通过 stack() 或类似重塑方法将其转换为长格式。

Netflix 数据清洗实战:credits 表处理

Section titled “Netflix 数据清洗实战:credits 表处理”
  • 清洗目标credits 表包含演职人员姓名、角色及作品 ID,通过清理缺失值和格式化,确保后续能与 titles 表准确关联。
  • 操作要点
    • 保持数据结构与原始表相似,但通过清理异常值和格式化列名,提高数据可读性。
    • 确保 person_idid 字段的完整性,这是进行表连接(Merge)的关键。
  • 分析与清洗的关联:数据清洗的所有决策(如删除行、填充缺失值)都必须服务于最终的分析目标(计算各流派演员平均 IMDb 评分)。
  • 清洗顺序:先确保结构整洁(Tidy Data),再处理内容质量(缺失值、异常值),最后进行统计分析。
  • 数据关联需求
    • cleaned_titles 包含影视作品信息,但缺乏演职员信息。
    • cleaned_credits 包含演职员信息,但缺乏作品流派信息。
    • 必须通过关联两表以实现“流派-演员-评分”的综合分析。
  • 连接键 (Key)
    • 两表均包含 id 变量,该字段代表影视作品的唯一标识,是进行 merge 操作的连接键。
  • 连接决策
    • 在进行连接前,必须明确连接方式(inner, outer, left, right),这取决于分析目标中对数据完整性的要求。
    • 决策依据:分析目标决定了连接方式,例如是否需要保留没有演职员信息的作品,或没有对应作品的演职员记录。
  • 连接策略
    • 使用 pd.merge() 函数将 cleaned_titlescleaned_credits 进行关联。
    • 连接键 (Key):使用 id 作为共同字段。
    • 连接方式 (How):需根据分析目标选择 inner(内连接)、outer(外连接)、left(左连接)或 right(右连接)。
    • 代码实现
credits\_with\_titles = pd.merge(cleaned\_credits, cleaned\_titles, on='id', how='inner')
- **连接后的价值**
- 连接后即可获得包含演员-流派-IMDb评分的完整数据集
- 通过 `head()` 方法可以快速预览合并后的结构确保数据对齐正确为后续的分组聚合分析计算平均评分做好准备
  • 操作逻辑:通过 pd.merge() 函数将 cleaned_creditscleaned_titles 两个 DataFrame 进行关联,以整合演职员信息与作品流派及评分数据。
  • 连接键 (Key):使用 id 作为共同字段,该字段在两表中均代表影视作品的唯一标识。
  • 连接方式 (how=‘inner’)
    • 决策依据:必须保留两表均存在的记录。若 ID 仅存在于 titles 中,则缺失演员信息;若仅存在于 credits 中,则缺失流派信息。
    • 采用内连接 (inner join) 可确保合并后的数据集包含完整的“流派-演员-评分”分析链路。
  • 代码实现
credits_with_titles = pd.merge(cleaned_credits, cleaned_titles, on='id', how='inner')
  • 验证:使用 head() 查看合并后的 DataFrame,确认数据对齐情况,为后续按流派分组计算演员平均 IMDb 评分做好准备。
  • 筛选需求:在合并后的数据集中,role 列包含 ACTOR(演员)和 DIRECTOR(导演)等多种身份,分析目标仅关注演员,因此需要过滤数据。
  • 实现方式:利用 query() 方法,直接筛选 role == 'ACTOR' 的行,并将结果保存为新变量供后续使用。

actor_with_titles = credits_with_titles.query(“role == ‘ACTOR’”)

  • 分组逻辑:为了计算各流派中演员的平均 IMDb 评分,需要按“流派”和“演员”进行分组。
  • 分组依据
    • 演员姓名(nameperson_id):person_id 比姓名更准确,可避免重名带来的统计偏差。
    • 影视流派(genres):已在前期清洗中完成拆分,确保每个流派独立。
  • 分析目标:通过分组聚合,找出特定流派中平均评分最高的演员,为选角决策提供数据支持。

使用 groupby() 进行多变量分组聚合

Section titled “使用 groupby() 进行多变量分组聚合”
  • 分组逻辑:为了计算各流派中演员的平均 IMDb 评分,需要先对流派进行分组,再对演员进行分组。
  • 分组键选择
    • 推荐使用 person_id 而非 name 进行分组,因为 person_id 是唯一标识,能有效避免重名或拼写错误导致的统计偏差。
  • 代码实现
grouped\_data = actor\_with\_titles.groupby(['genres', 'person\_id'])
- **聚合分析**
- 分组后可对 IMDb 评分列调用聚合函数 `mean()`计算平均值
- 最终目标是识别出每个流派中平均评分最高的演员为选角决策提供依据
  • 计算逻辑:在完成按流派 (genres) 和演员 ID (person_id) 分组后,对 imdb_score 列调用 mean() 函数。
  • 代码实现

计算每个流派中每位演员的平均 IMDb 评分

Section titled “计算每个流派中每位演员的平均 IMDb 评分”

imdb_score_grouped = grouped_data[‘imdb_score’].mean()

- **结果结构**
- 结果是一个包含多层索引MultiIndex Series
- 外层索引为 `genres`内层索引为 `person_id`
- 这种结构虽然清晰但由于索引是层级化的直接操作可能不如常规 DataFrame 方便
  • 问题背景:当前的 imdb_score_grouped 是一个带有层级索引的 Series,如果需要将 genresperson_id 变回普通列以便后续筛选或排序,需要进行索引重置。
  • 解决方案:使用 reset_index() 方法将索引转换为列。
  • 操作示例

将层级索引重置为普通列,转换回 DataFrame

Section titled “将层级索引重置为普通列,转换回 DataFrame”

imdb_score_df = imdb_score_grouped.reset_index()

- **转换意义**重置索引后数据变回标准的 DataFrame 格式每一行代表一个流派-演员组合及其对应的平均评分便于后续直接使用 `sort_values()` 查找特定流派中评分最高的演员
  • 继续使用现有 DataFrame

  • 目标:查找各流派(如动作片)中 RMDBScore 的最大值。

  • 然后回查最高分对应的演员。

  • 已知各流派平均最高分,但不知具体演员 → 通过 PACEIDRMDBScore 对值,与前 DataFrame 再次连接。

  • 聚合注意事项:分组后聚合操作(如max())会应用于所有列。若包含person_id,会计算max(person_id),但这无意义。

  • 正确步骤

    1. 仅对imdb_score每流派最大值(排除person_id)。
    2. 双键连接genres + imdb_score)将最大值结果与原详细DataFrame合并,回查对应person_id和演员姓名。
  • 连接键genres(流派)和imdb_score(最高平均分),确保匹配精确演员。

查找各流派最高分演员的逻辑优化

Section titled “查找各流派最高分演员的逻辑优化”
  • 分组聚合的陷阱:直接在 groupby 后对整个 DataFrame 使用聚合函数(如 .max())会导致所有列(包括 person_id)都被执行最大值计算,这在逻辑上是无意义的。

  • 正确查找路径

    1. 提取最大值:仅对 imdb_score 列求各流派的最大值(grouped_data['imdb_score'].max()),得到各流派的最高平均分。
    2. 双键连接回查:将上述结果与包含详细演员信息的 DataFrame 进行 merge,连接键设为 ['genres', 'imdb_score']
    3. 获取目标演员:通过双键匹配,即可准确回溯到该流派最高分所对应的 person_id 和演员姓名,避免了错误计算 person_id 的最大值。

最高分演员姓名关联与排序优化

Section titled “最高分演员姓名关联与排序优化”
  • 先提取 person_id(PACEID)。
  • 删除重复记录,确保演员ID与名字一一对应。
  • 将演员ID和名字数据与前面最高分数据连接。
  • 连接键PACEID
  • 结果:获取各流派平均分最高演员名字
  • 当前结果:不同流派顺序混乱,按演员名字排序,不便一眼看出各流派最高分演员。
  • 解决方案:使用 DataFrame.sort_values() 方法,按指定变量排序(如评分)。
  • 示例:传入排序变量,查看这张网(表格)。
  • 数据准备
    • 从原始 cleaned_credits 中提取 person_idname 列。
    • 使用 drop_duplicates() 去除重复项,确保每个 person_id 仅对应一个唯一姓名,防止连接后数据冗余。
  • 连接操作
    • 将处理后的演员姓名 DataFrame 与包含各流派最高平均分的 DataFrame 进行 merge
    • 连接键person_id
  • 排序优化
    • 原始结果默认按演员姓名排序,不便于查看各流派表现。
    • 使用 sort_values() 方法按评分(或流派)进行排序,使各流派最高分演员一目了然。
  • 实战价值:本次为复杂数据整理实践,接近数据分析,帮助复习连接分组等技巧。
  • 益处:深化数据探索能力,有利于后续分析。
  • 下一步:尝试其他感兴趣数据集,进行数据整理练习。
  • 社区互动:分享作品,或为同学作品提供建议/反馈,共同进步。
  • 统计学本质:对数据进行描述(特征概述)和推断
  • 本节重点描述统计学,后续涉及推断统计。
  • 数据分类
    • 分类数据:有限类别,如性别(二元)、狗品种(多种但有限)。
    • 数值数据:具体测量值。
  • 数值数据(Numerical Data)不仅表示数值,还可以进行数学运算和统计分析(如求和、平均值)。
  • 示例:班级人数、身高、工资。
  • 注意:对分类数据(如性别、金牌/银牌/铜牌)进行数学运算往往没有实际意义。

分类数据与数值数据的进一步细分

Section titled “分类数据与数值数据的进一步细分”
数据类型 细分类别 特点
分类数据 定序数据 (Ordinal) 有明确的顺序(如:金 > 银 > 铜)
分类数据 定类数据 (Nominal) 无顺序之分(如:狗的品种)
数值数据 连续数据 (Continuous) 没有最小表示单位,两数值间可取无限值(如:头发长度)
数值数据 离散数据 (Discrete) 只能以整数或自然数为单位(如:台数、人数)
  • 集中趋势 (Central Tendency):观察数据集中在何处,即数据的“中心”位置。
  • 离散趋势 (Dispersion/Spread):与集中趋势相反,衡量数据偏离中心的散布情况。
  • 分布形状 (Distribution Shape):描述数据分布的对称性、峰值高低等形态特征。
  • 钟型分布:中间高、两头低,左右对称。
  • 应用示例:高考分数人数分布,通常呈现中间集中、两端稀少的钟型曲线。
  • 确定分析维度(集中、离散、分布)。
  • 计算对应的统计学指标(如平均值、标准差等)。
  • 通过可视化图表(如直方图)直观判断分布形态。
  • 定义:衡量数据集中在何处,即数据的“中心”位置。
  • 分析指标
    • 平均数 (Mean):所有数值之和除以数值个数,代表数据的中心位置。容易受极端值(如姚明身高)影响。
    • 中位数 (Median):将数据按大小排序后处于中间的数值。若数据个数为偶数,则取中间两个数的平均值。不易受极端值干扰。
    • 众数 (Mode):数据中出现次数最多的数值。
  • 位置意义:正好将数据分为两半,一半大于它,一半小于它。
  • 鲁棒性:相比平均数,中位数对极端值不敏感,在统计分析中常与平均数结合使用。

  • 定义:衡量数据偏离中心位置的散布情况,与集中趋势相对。
  • 定义:描述数据分布的形态特征,如对称性、峰值高低等。

离散趋势 (Dispersion/Spread)指标详解

Section titled “离散趋势 (Dispersion/Spread)指标详解”
  • 衡量数据偏离中心程度。
  • 可用指标:
    • 极差最大值 - 最小值,表示数据波动范围。
    • 方差/标准差:可相互转换,公式更复杂。
      • 方差符号:σ²
      • 标准差符号:σ
      • 方差 = 标准差的平方,标准差 = 方差的平方根。
    • 四分位距