Chapter 1 魔法学院开学与数据初体验 / Welcome to Magic Academy: First Encounter with Data
阳光像撒了把碎金,落在数据魔法学院的尖顶红砖上,橡木大门上的黄铜门环被擦得亮闪闪的,旁边站着三只歪着脑袋的猫头鹰,爪子上攥着烫金的录取通知书,羽毛上还沾着刚送信时蹭的云朵絮。九岁的小宇攥着妈妈缝的卡通书包带,眼睛亮得像装了星星——他上周还在家拆旧计算器研究数字规律,今天就拿到了数据魔法学院的入学通知,旁边的小美正踮着脚看门廊下飘的魔法彩带,粉色的裙摆沾了点草屑,阿明则偷偷摸出口袋里的橘子糖,还没剥就被小宇拍了一下手背:“嘘!麦斯教授要讲话啦!”
“同学们好呀!”麦斯教授的声音像加了棉花糖,软乎乎的,他胸前的二进制刺绣代码一闪一闪的,“Welcome to Data Magic Academy! 从今天起,你们就是数据魔法学院一年级的小魔法师啦!”他手里的触控笔教鞭轻轻敲了敲讲台,悬浮的黑板上立刻跳出来学院的logo:一个装着星星的魔法盒子,旁边飘着一行小字“用数据点亮魔法世界”。台下的小魔法师们立刻爆发出欢呼,几个胆子大的还伸手去抓袍子上飘出来的二进制小星星,抓到的同学开心地举着给旁边的人看。
等欢呼声小了点,麦斯教授晃了晃手里卷成筒的羊皮纸:“开学第一课,我要给你们布置第一个超有意思的小任务!下周我们一年级要去森林公园春游,需要你们帮忙统计全班42名同学的物资需求:每人2包零食,每人500毫升饮用水,常备药品15种各准备1份,还要注意有没有同学有特殊的饮食或者用药需求哦~”他话音刚落,三个小脑袋就凑到了一起——小宇数学最好,小美做事最细心,阿明点子最多,三个人主动组成了“统计小分队”,蹦蹦跳跳地跑到教室外面的空地上准备开工。
“我们先数零食吧!”小宇掏出妈妈给的小记事本,拔开笔帽,“我数你记,这样不会错。”阿明自告奋勇当“计数器”,蹲在成箱的零食前面开始数:“1包、2包、3包…”数到第27包的时候,同班的乐乐跑过来问小宇数学题,小宇转头给他讲题讲了5分钟,再回头问阿明:“数到哪了?”阿明挠挠头:“我、我也忘了…刚才数的时候有个同学递给我一块巧克力,我吃了就忘了数到哪了!”小美赶紧拿出画本画正字,刚画了三个“正”,一阵风吹过来把画本吹到了地上,她捡起来的时候发现纸湿了一角,画的“正”字都晕开了。
三个人折腾了快四十分钟,最后凑出来的数字是:零食90包,饮用水19000毫升,常备药品15份。小宇皱着眉算了一遍:“不对啊,42个同学每人2包零食应该是84包,怎么多了6包?饮用水每人500毫升应该是21000毫升,怎么少了2000毫升?”阿明抓抓头:“会不会是我刚才数的时候把几包混在一起了?或者我吃的那包零食算重复了?”小美也急了:“我们画的正字都花了,根本不知道数了多少,重新数又要好久,春游快来不及啦!”
正当三个人围着写满数字的纸团团转的时候,麦斯教授端着三杯橘子汽水走了过来,看到他们皱巴巴的纸,笑着揉了揉阿明的头发:“遇到麻烦啦?要不要试试你们的第一个魔法工具——NumPy(/ˈnʌmpaɪ/,全称Numerical Python,数值Python)?它是所有数据魔法的基础,就像你们做手工需要剪刀胶水一样,做数据魔法必须用到它。”
麦斯教授挥了挥触控笔,半空中立刻跳出来一个发光的卷轴,上面飘着密密麻麻的数字和代码,小宇眼睛一亮:“这就是NumPy魔法卷轴吗?好漂亮啊!”麦斯教授点头:“对啊,它最厉害的工具就是数组(Array),你可以把它当成一个魔法收纳盒,每个格子只能放一个数字,格子上的编号就是索引(Index),从0开始计数,找数字的时候超快,绝对不会乱。比如我们要统计的三种物资,就可以放在一个有3个格子的收纳盒里:第一个格子放零食包数,第二个放饮用水毫升数,第三个放常备药品份数。”
他点了一下卷轴,代码立刻跳了出来,旁边还配了中文注释,就像魔法咒语的说明: ```python
导入NumPy魔法工具包,就像打开魔法收纳盒的钥匙
import numpy as np
创建物资收纳盒(数组):第一个是零食包数,第二个是饮用水毫升数,第三个是常备药品份数
supplies = np.array([84, 21000, 15]) ``` “你们看,”麦斯教授指着屏幕上跳出来的数字,“第一个84就是42个同学每人2包零食的总数,第二个21000是42个同学每人500毫升饮水的总数,第三个15是常备药品的份数,每个数字都放在对应的格子里,绝对不会乱。”
小宇赶紧掏出自己的小平板(数据魔法学院给每个小魔法师都准备了专用的学习平板),照着代码输入,刚按回车,屏幕上就跳出来三个数字:`[84 21000 15]`,他惊讶地睁大眼睛:“哇!真的出来了!我之前数了半小时都没数对,现在一下子就出来了!”小美也试着输入代码,她发现如果要给3位随行的老师也准备一份物资,不用一个个改数字,只要在数组后面加个加法咒语就行: ```python
给老师也准备一份物资,用加法魔法咒语
supplies_for_teachers = supplies + 3 print(supplies_for_teachers) ``` 屏幕上跳出来`[87 21003 18]`,小美拍手:“哇!这样加的话,每个格子里的数字都自动加3,不用一个个改了,太方便了!”阿明则试着用了求和魔法咒语`np.sum()`,刚按回车就跳出来`21099`,他开心地跳起来:“总和一下子就出来了!我之前加零食加错了三次,现在不会错了!”
麦斯教授又教了他们几个超实用的魔法咒语:“想知道平均每种物资有多少,就用`np.mean()`;想知道哪个物资最多、哪个最少,就用`np.max()`和`np.min()`,比你们手动画正字快多啦!”小宇试了`np.mean()`,出来`7033`,他推推眼镜说:“平均每种物资有7033份,要是我们挨个算的话,要算好久的!”阿明试了`np.max()`和`np.min()`,出来最大值是21000,最小值是15,他挠挠头:“哦!原来饮用水最多,药品最少,和我们之前数的一样,但是只用了1秒钟!”
这时候小美突然举手:“麦斯教授,我们是不是忘了算有过敏的同学?我之前听班长说有3个同学花生过敏,2个芒果过敏,还有5个对其他食物过敏,要给他们准备特殊的抗过敏药,而且零食不能有芒果味的呀!我们之前的统计里没算这些!”麦斯教授眼睛一亮,带头给小美鼓掌:“非常棒!你观察得很仔细,这就是逐个计数的最大缺点——很容易漏掉特殊的小细节。那我们再加一个特殊需求的数组吧!”
他点了一下卷轴,新的代码跳了出来: ```python
创建特殊需求收纳盒(数组):第一个是花生过敏人数,第二个是芒果过敏人数,第三个是其他过敏人数
special_needs = np.array([3, 2, 5])
用求和魔法咒语算特殊需求总人数
total_special = np.sum(special_needs) print("有特殊需求的同学人数:", total_special) ``` 屏幕上跳出来`10`,小宇恍然大悟:“哦!原来有10个同学有特殊需求,我们之前只算了普通同学,漏了他们,太感谢小美啦!”阿明也拍脑袋:“对啊!要是给过敏的同学吃了芒果味的零食,他们会难受的,我们差点闯祸啦!”
三个人赶紧更新了物资清单:常备药品从15份改成25份,多准备10份抗过敏药,零食去掉所有芒果味的,最后用`np.sum(supplies)`算出来的总物资份数是`21109`,和麦斯教授之前说的完全对得上!麦斯教授笑着给他们每人发了一枚亮闪闪的徽章,上面刻着“数据小能手”的字样,旁边还飘着小小的二进制代码。这时候数据猫头鹰“扑棱棱”飞了过来,给每个人递了一个魔法笔记本,封面上印着NumPy的logo,阿明赶紧把口袋里剩下的橘子糖递给猫头鹰,猫头鹰开心地蹭了蹭他的手,叼着糖飞走了。
--- > 💡 电子版魔法小贴士 > 数组的索引从0开始哦!就像你家的门牌号从0号开始,不是1号,这样计算机找数字的时候会更快~要是你记不住,就想“第一个格子是0号,就像比赛的第一名是冠军,不是第0名,但是计算机的计数是从0开始的哦”! > > 📖 纸书脚注 > ① 索引(Index):数组中每个元素的编号,从0开始计数,是计算机存储和查找数据的重要方式,也是所有编程语言的基础规则哦。
--- > 📝 动手小实验:我的零食消耗魔法日记 > 材料:笔记本、铅笔、家里的零食柜、电脑/平板(可选) > 步骤: > 1. 连续记录7天(周一到周日)每天吃的零食包数,比如周一吃了1包薯片,周二吃了2包糖果,周三没吃零食就记0,把所有数字都记在笔记本上 > 2. 如果你已经学会了NumPy魔法,就把这7天的数字做成一个数组,比如`snacks = np.array([1, 2, 0, 3, 1, 4, 2])` > 3. 用`np.sum(snacks)`算出一周总共吃了多少零食,用`np.mean(snacks)`算出平均每天吃多少 > 4. 用`np.max(snacks)`和`np.min(snacks)`找出吃的最多和最少的哪天,想想为什么那天吃的多?(比如周末看了动画片?还是家里来了客人?) > 5. 小挑战:如果妈妈要求你下周每天最多吃2包零食,算算你需要把每天的零食量减少多少,用数组运算试试!比如这周你周一吃了3包,就要减少1包,写成`reduce = snacks - 2`,正数就是要减少的量,负数就是还没超量哦~ > > 记得把你的实验结果写在魔法笔记本上,下节课可以和同学分享哦!
---
本章魔法知识点回顾(中英双语)
1. NumPy(/ˈnʌmpaɪ/):数值
阿明刚把代码抄完,指尖还沾着橘子汽水的甜味,突然想起刚才数零食的时候漏了过敏同学的细节,赶紧举爪子问:“教授教授!我们刚才算的物资里,是不是还要减去过敏同学不能吃的零食呀?比如芒果味的零食有20包,2个同学芒果过敏,那这20包是不是不能算进去?”麦斯教授眼睛一亮,指尖的触控笔在空中划出个 glowing 的小勾:“太棒啦!你已经学会举一反三啦!这就是数据魔法里最常用的‘条件筛选’小咒语,我们现在用NumPy的小魔法就能算出来。”
他点开的卷轴上跳出来新的代码,旁边还配了可爱的动画:三个小格子分别装着芒果味零食、花生味零食、普通零食,过敏的同学只能拿普通和无花生的: ```python
原来的零食总数
total_snacks = 84
芒果味零食20包,花生味零食15包,普通零食49包
snack_types = np.array([20, 15, 49])
芒果过敏2人,每人1包芒果味不能吃;花生过敏3人,每人1包花生味不能吃
cant_eat = np.array([2, 3, 0])
实际能吃的零食总数 = 总零食 - 不能吃的零食
available_snacks = total_snacks - np.sum(cant_eat) print("实际能吃的零食总数:", available_snacks, "包") ``` 屏幕上跳出来`79`,小美掰着手指算了一遍:“没错!20包芒果味减去2包,15包花生味减去3包,剩下的49包普通零食都能吃,加起来刚好是79包!”阿明拍了下脑袋:“哦!我之前还担心会浪费零食,现在算出来刚好够,不会多买啦!”
这时候阿明又遇到个小问题:他刚才输入代码的时候不小心把`arange`拼成了`arang`,屏幕立刻跳出来红色的错误提示`NameError: name 'arang' is not defined`,他不好意思地挠挠头:“我、我拼错啦…”麦斯教授笑着点点他的脑袋:“对啦!写代码就像写魔法咒语,哪怕一个字母写错,魔法都会失效哦,以后写代码要仔细检查拼写呀!”阿明赶紧把拼写改过来,屏幕上立刻跳出来正确的1到42的学号数组,他松了口气,赶紧把`arange`的拼写用荧光笔标在魔法笔记本上,旁边还画了个小橘子当记号。
小宇则对数组的索引特别感兴趣,他试着输入`print(supplies[1])`,屏幕上立刻跳出来`21000`,他推推眼镜说:“教授,我懂了!索引就是收纳盒的门牌号,0号是零食,1号是饮用水,2号是药品,我只要说门牌号就能拿到对应的数字,比翻整个收纳盒快多啦!那如果我要拿前两种物资,是不是可以用切片魔法?”麦斯教授点头,点出来的代码刚好验证了他的想法: ```python
切片魔法:拿0号到2号之前的物资(也就是0号和1号)
print(supplies[0:2])
步长为2的切片:拿0号和2号物资,跳过1号
print(supplies[0:3:2]) ``` 屏幕上依次跳出来`[84 21000]`和`[84 15]`,小美惊喜地说:“哇!切片就像切蛋糕,我想吃哪块就切哪块,太方便啦!要是我们有100种物资,我只要切片就能拿到我需要的,不用翻遍整个收纳盒!”
麦斯教授又教了他们几个快速创建数组的实用咒语,他说:“有时候你们不知道具体的数字,只想先做个空收纳盒占位置,或者要生成连续的数字,这些咒语能帮你省好多时间。”他点出来的代码旁边还配了动画:空收纳盒一施法就自动填满了数字,就像变魔术一样。 ```python
魔法1:创建3个格子全是0的空收纳盒,用来先占位置,后面再填数字
dtype=int 表示格子里存整数,不加的话默认存带小数点的浮点数
empty_box = np.zeros(3, dtype=int) print("空收纳盒:", empty_box) # 输出:[0 0 0]
魔法2:创建3个格子全是1的试吃装收纳盒,每种物资先准备1份试吃
taste_box = np.ones(3, dtype=int) print("试吃装收纳盒:", taste_box) # 输出:[1 1 1]
魔法3:自动生成1到42的连续数字,给每个同学编学号
arange的规则是:从第一个数开始,到第二个数之前结束,步长默认是1
student_ids = np.arange(1, 43) print("同学学号:", student_ids) # 输出:[1 2 3 ... 41 42] ``` 阿明试了`np.arange(1, 43, 2)`,屏幕上跳出来1、3、5…41,他歪着头问:“教授,这个2是什么呀?”麦斯教授解释:“这是步长魔法,就是每次跳几个数,步长是2的话,就是每次跳2个数,只留奇数,要是步长是3的话,就是1、4、7…这样,你要是想给学号是3的倍数的同学准备小礼物,就可以
阿明指尖刚敲完回车键,屏幕上立刻跳出来一串歪歪扭扭的数字:1、3、5、7…一直跳到41,最后一个数字还闪着淡金色的光,像排队做核酸的小企鹅,他歪着脑袋戳了戳屏幕:“教授教授!这个步长是2的话,就只留奇数啦?那步长是3的话,是不是就是3、6、9…这样?”
麦斯教授笑着点头,指尖的触控笔在空中划出个小小的彩虹弧:“太聪明啦!步长就像你跳房子的时候每次跳几个格子,步长是3的话,就每次跳3个格子,刚好能找出所有学号是3的倍数的同学——等下我们发小贴纸的时候,就可以用这个咒语快速找出哪些同学能拿到哦!”他点出来的代码刚跳出来,阿明就抢着输入: ```python
步长为3,生成3到42的3的倍数数组,给对应同学发小贴纸
gift_student_ids = np.arange(3, 43, 3) print("能拿到小贴纸的同学学号:", gift_student_ids) ``` 屏幕上跳出来`[3 6 9 12 15 18 21 24 27 30 33 36 39 42]`,一共14个数字,阿明拍了下手:“刚好14个同学!我之前要一个个数学号,要数好久的,现在一下子就出来了!”
小宇则对数组的长度特别感兴趣,他想起刚才算42个同学,要是不知道有多少人,就没法算每人分多少零食,于是举着平板问:“教授,有没有什么咒语能直接知道数组里有多少个数字呀?”麦斯教授指尖一点,卷轴上跳出来个小小的计数器图标:“当然有!这就是`len()`长度魔法咒语,就像你数收纳盒里有多少个格子一样,一用就知道。”小宇赶紧输入`len(gift_student_ids)`,屏幕跳出来`14`,他推推眼镜算了一遍:“3的倍数从3到42,一共14个,没错!那如果我要算全班42个同学,用`len(student_ids)`是不是就是42?”他输入之后果然跳出来`42`,开心地晃了晃脑袋:“我之前数班级人数还要点名,现在只要用这个咒语,1秒钟就知道有多少人啦!”
这时候小美抱着自己的魔法笔记本凑过来,她的粉色平板上贴着自己画的蝴蝶贴纸,刚才她把索引从0开始的规则画成了小图:三个并排的魔法收纳盒,下面分别标着0、1、2,旁边画了个小箭头写着“门牌号从0开始哦”,她问:“教授,我们刚才的物资数组`supplies`里有3个数字,要是我想知道每种物资占总数量的百分之多少,是不是也能用数组算呀?”
麦斯教授眼睛一亮,给小美比了个大拇指:“太棒啦!你已经想到用数据算比例啦!我们只要把数组里的每个数字都除以总数量,就能得到比例啦!”他点出来的代码旁边配了小小的扇形图动画,三种物资的占比一目了然: ```python
总物资数量
total_supplies = np.sum(supplies)
每种物资的占比,乘以100变成百分比
supplies_ratio = (supplies / total_supplies) * 100 print("零食占比:", round(supplies_ratio[0], 1), "%") print("饮用水占比:", round(supplies_ratio[1], 1), "%") print("药品占比:", round(supplies_ratio[2], 1), "%") ``` 屏幕跳出来`零食占比:0.4 %`、`饮用水占比:99.5 %`、`药品占比:0.1 %`,阿明惊讶地张大了嘴:“哇!原来饮用水占了几乎全部!我之前还以为零食最多呢,因为零食的包数看起来最多,没想到毫升数加起来那么大!”小美也点头:“没错!要是我们只看包数,会误以为零食最多,但是看占比就知道饮用水才是最多的,这样买的时候就不会买错啦!”
麦斯教授又教了他们两个超实用的数组创建咒语,他说:“有时候你们不需要自己一个个输数字,只要告诉NumPy你要什么样的收纳盒,它就能自动帮你做出来哦!”他点出来的代码旁边配了动画:空收纳盒一施法就自动填满了数字,像变魔术一样: ```python
魔法1:np.zeros(格子数) → 创建全是0的收纳盒,用来先占位置,后面再填数字
dtype=int 表示格子里存整数(没有小数点的数,比如1、2、3),不加的话默认存带小数点的浮点数(比如1.5、2.3)
empty_box = np.zeros(3, dtype=int) print("空收纳盒:", empty_box) # 输出:[0 0 0]
魔法2:np.ones(格子数) → 创建全是1的试吃装收纳盒,每种物资先准备1份试吃
taste_box = np.ones(3, dtype=int) print("试吃装收纳盒:", tas