列表与统计
列表把多个值集中放在一个名称下,比如一周的步数、一组考试分数或一个月的收据。把值放进列表后,你就可以求和、求平均值、衡量它们的离散程度,并挑出重要的值。
本页介绍如何书写列表和按索引取值、汇总统计、排序与切片、用 map 和 filter 变换列表,以及对整个列表进行运算。每当你有一列数字需要汇总时,都可以参考本页。由行组成的列表还可以当作矩阵使用,矩阵有专门的页面介绍。
书写列表
把值放在方括号里,用逗号分隔:
成绩 = [72, 85, 91, 64, 88] 结果为 [72, 85, 91, 64, 88]成绩[0] 结果为 72成绩[4] 结果为 88length(成绩) 结果为 5值在列表中的位置称为索引,写在列表后面的方括号里。索引从零开始,所以 成绩[0] 是第一个分数,成绩[4] 是第五个。最后一个元素总是位于 length(成绩) - 1。负数、小数或超出范围的索引都会报错;索引不会从末尾倒着绕回来。
元素可以是计算式、带单位的数量,也可以是其他列表:
[2 * 45, 60 + 15] 结果为 [90, 75][1.5 kg, 750 g] 结果为 [1.5 kg, 750 g][[1, 2], [3, 4]][1][0] 结果为 3对于嵌套列表,第一个索引选出内层列表,第二个索引再从中选出一个值:[1] 选中 [3, 4],然后 [0] 选中 3。
千位分隔符在列表中同样有效,所以你可以按数字原本的写法直接粘贴。在值与值之间的每个逗号后面加一个空格;逗号后面紧跟三位数字时,仍然算作同一个数字的一部分:
工资 = [£32,000, £41,500, £38,250] 结果为 [£32,000.00, £41,500.00, £38,250.00]average(工资) 结果为 £37,250.00有几点需要注意:
- 不加空格时,每个逗号都会分隔值,所以
[8200,10400]包含两个值。像[100,200,300]这样的列表既可能是一个数字,也可能是三个数字,因此 Varlig 会显示错误,并写明两种可能的含义。 - 结果会显示千位分隔符,并在每个逗号后面加一个空格,所以
[8,200, 10,400]是包含两个值的列表。 - 一个列表最多包含 1,024 个值。
汇总统计
成绩 = [72, 85, 91, 64, 88]sum(成绩) 结果为 400average(成绩) 结果为 80median(成绩) 结果为 85min(成绩) 结果为 64max(成绩) 结果为 91count(成绩) 结果为 5mean 和 avg 是 average 的别名,total 是 sum 的别名,stdev 是 stddev 的别名,count 是 length 的别名,所以电子表格里的习惯写法可以照搬过来。product 把所有值相乘。没有百分位数函数;median 给出的是中间值。
对于命名的列表,你也可以用文字写出统计量:
成绩 = [72, 85, 91, 64, 88]average of 成绩 结果为 80total of 成绩 结果为 400sum of、mean of、median of、min of、max of 和 count of 的用法相同。在 of 后面写一个名称,或者直接写出用逗号分隔的值,加不加圆括号都可以。如果值写在方括号里,请使用函数形式,如 average([72, 85, 91])。
大多数统计函数,包括 sum、product、average、median、min、max、count 和 stddev,也接受分开书写的值,而不一定是列表:
max(72, 85, 91) 结果为 91average of 70, 85, 91 结果为 82mean of (1,2,3) 结果为 2average(1,200, 1,800) 结果为 1,500合计上方的行
只写了 sum 或 total 的一行,会把它上方的答案加起来,一直加到上一个 sum 或 total 行或者 --- 分隔线为止。它的作用与某一行快捷菜单中的 Subtotal(小计)选项相同,所以笔记中的每个部分都能在你输入时得到各自的合计:
# 食品杂货面包 = £1.20牛奶 = £1.45鸡蛋 = £2.30total 结果为 £4.95# 日用品垃圾袋 = £3.50洗洁精 = £1.80total 结果为 £5.30如果你给某个值起名为 sum 或 total,这一行会显示你的值。
离散程度:方差与标准差
成绩 = [72, 85, 91, 64, 88]variance(成绩) 结果为 132.5stddev(成绩) to 1 dp 结果为 11.5两者都是样本统计量:它们除以值的个数减一(n - 1),当你的数据是从更大整体中抽取的样本时,通常这样计算。它们至少需要两个值。在任何统计量后面加上 to 1 dp 即可舍入。
如果你的列表就是整个总体,则应改为除以 n。可以用下文介绍的 map 来构建:
# 列车延误分钟数延误 = [2, 4, 4, 4, 5, 5, 7, 9]stddev(延误) 结果为 2.1380899353中心 = average(延误) 结果为 5sqrt(sum(map((x - 中心)^2, x, 延误)) / count(延误)) 结果为 2带单位的列表
sum、average、median、min、max 和 stddev 会保留单位,并在兼容的单位之间换算:
通勤 = [24 min, 31 min, 27 min, 22 min]sum(通勤) 结果为 104 minaverage(通勤) 结果为 26 minmedian(通勤) 结果为 25.5 minsum([1 kg, 500 g]) 结果为 1.5 kgvariance 只接受普通数字,因为它的结果会是单位的平方。sort 同样只适用于普通数字。对单位不兼容的值(如 sum([1 kg, 3 m]))进行统计会报错,而不会悄悄跳过某些值。
空列表
| 对空列表调用 | 结果 |
|---|---|
sum([]) |
0 |
product([]) |
1 |
count([]), length([]) |
0 |
average([]), median([]), min([]), max([]) |
错误 |
variance([]), stddev([]) |
错误 |
空列表最常见于没有匹配到任何值的 filter 的结果,所以即使原始列表有值,对筛选后的列表求 average 也可能失败。
列表函数参考
| 调用 | 作用 |
|---|---|
range(start,end) |
从 start 到 end 的整数,包含两端 |
range(start,end,step) |
以 step 为步长计数,步长可以是负数或小数,但不能为零 |
length(list), count(list) |
值的个数 |
sort(list) |
从小到大排序;仅限普通数字 |
reverse(list) |
把相同的值按相反顺序排列;适用于任何值 |
unique(list) |
每个值只保留第一次出现的那个 |
slice(list,start,end) |
从索引 start 开始、到索引 end 之前(不含)的值 |
map(expression,variable,list) |
对每个值计算一次表达式 |
filter(condition,variable,list) |
保留使条件为 true 的值 |
range(1, 5) 结果为 [1, 2, 3, 4, 5]range(0, 100, 25) 结果为 [0, 25, 50, 75, 100]range(10, 0, -2) 结果为 [10, 8, 6, 4, 2, 0]成绩 = [72, 85, 91, 64, 88]sort(成绩) 结果为 [64, 72, 85, 88, 91]reverse(sort(成绩)) 结果为 [91, 88, 85, 72, 64]slice(sort(成绩), 0, 3) 结果为 [64, 72, 85]unique([4, 2, 4, 6, 2]) 结果为 [4, 2, 6]reverse(sort(…)) 让最大的值排在最前面,slice(sort(…), 0, 3) 则挑出最低的三个。
切片边界必须满足 0 <= start <= end <= length。start 与 end 相等时得到空列表。没有 成绩[0:2] 这种简写;请写 slice(成绩, 0, 2)。
unique 按值的存储形式精确比较,所以 1 kg 和 1000 g 算作两个不同的值,在小数点后第十位不同的两个结果也不会合并。
map 与 filter
map 对列表中的每个值计算一个表达式。filter 保留通过测试的值。两者都是先写表达式,再写代表每个值的名称,最后写列表:
成绩 = [72, 85, 91, 64, 88]map(s + 5, s, 成绩) 结果为 [77, 90, 96, 69, 93]filter(s >= 80, s, 成绩) 结果为 [85, 91, 88]count(filter(s >= 80, s, 成绩)) 结果为 3average(filter(s >= 80, s, 成绩)) 结果为 88filter(s >= 80, s, 成绩) 可以读作“成绩 中满足 s >= 80 的值 s”。两者都适用于单位和金额:
map(p * 1.2, p, [£10, £25, £40]) 结果为 [£12.00, £30.00, £48.00]支出 = [£42.50, £18.20, £65.00, £23.30]filter(x > £30, x, 支出) 结果为 [£42.50, £65.00]sum(filter(x > £30, x, 支出)) 结果为 £107.50你选的名称只在这次调用内部有效,不会改变笔记中其他地方同名的值:
x = 10map(x * 2, x, [1, 2, 3]) 结果为 [2, 4, 6]x 结果为 10filter 中的条件必须是比较式或其他真/假值。普通数字不会被当作真,所以 filter(x, x, [0, 1, 2]) 会报错。对空列表执行 map 或 filter,得到的仍是空列表。
列表运算
你可以把每个值都乘以或除以同一个数,也可以把两个长度相同的列表逐值相加或相减:
[30, 36, 22] / 40 * 100 结果为 [75, 90, 55]2 * [3, 1, 4] 结果为 [6, 2, 8]-[5, -3] 结果为 [-5, 3]模考 = [62, 70, 60]期末 = [72, 78, 69]期末 - 模考 结果为 [10, 8, 9]average(期末 - 模考) 结果为 9[1 kg, 2 kg] + [500 g, 500 g] 结果为 [1.5 kg, 2.5 kg][72, 85] == [72, 85] 结果为 true第一行把满分 40 的分数换算成百分比。期末 - 模考 这一行把每位学生的模考分数与期末分数一一对应。
有些运算不支持列表:
- 给列表加上单个数字,如
成绩 + 5。请改用map(s + 5, s, 成绩)。 - 把长度不同的列表相加。值永远不会为了凑齐长度而重复。
- 用
*把两个平面列表相乘。要计算逐对乘积之和,请使用dot:dot([2, 3, 1], [4, 5, 10])是33,比如两杯单价 4 的咖啡、三杯单价 5 的茶和一块单价 10 的蛋糕。对于由行组成的列表,*表示矩阵乘法,请参阅向量与矩阵。
由行组成的列表
由行组成的列表就像一张小表格。average 和 sum 按列计算,map 则可以逐行计算:
# 每行是一名学生:[模考, 期末]成绩单 = [[62, 72], [70, 78], [60, 69]]average(成绩单) 结果为 [64, 73]sum(成绩单) 结果为 [192, 219]map(average(r), r, 成绩单) 结果为 [67, 74, 64.5]median、min、max 和 stddev 不接受由行组成的列表。请像最后一行那样用 map 逐行处理,或者把某一列单独提取成一个列表。
所有这些运算的确切规则,请参阅高等数学:功能与限制参考页。
综合运用
下面是手表记录的一周步数,以及每日目标:
# 步数,周一至周日步数 = [8200, 10400, 6300, 12000, 9100, 7400, 9600]目标 = 9000sum(步数) 结果为 63,000average(步数) 结果为 9,000median(步数) 结果为 9,100达标天数: count(filter(s >= 目标, s, 步数)) 结果为 4极差: max(步数) - min(步数) 结果为 5,700# 每步约 0.75 m步行距离 = sum(步数) * 0.75 m步行距离 in km 结果为 47.25 km平均值恰好等于目标,但只有四天达标,而且最好和最差的两天相差 5,700 步。修改 目标,或者粘贴下一周的步数,每一行都会随之更新。