函数(Functions)
- 关于函数的基本定义与调用从略(可见CS61A),下面只说明一些函数的进阶用法。
同样,本节也会用R语言作为对照。
将函数作用于列
- 在之前的表格操作中,我们常常在创建新列时将已有列作为数组传入函数中进行转化运算。但是一些函数可能无法直接接受数组参数,而只能接受单个数字。
- 比如下面这个例子:
import pandas as pd
ages = pd.DataFrame({
'Person': ['A', 'B', 'C', 'D', 'E', 'F'],
'Age': [17, 117, 52, 100, 6, 101]
})
agesages <- data.frame(
Person = c('A', 'B', 'C', 'D', 'E', 'F'),
Age = c(17, 117, 52, 100, 6, 101)
)
ages| Person | Age |
|---|---|
| A | 17 |
| B | 117 |
| C | 52 |
| D | 100 |
| E | 6 |
| F | 101 |
- 现在我们希望在创建新列时使用以下函数:
def cut_off_at_100(x):
"""取x与100中的较小值。"""
return min(x, 100)cut_off_at_100 <- function(x) {
return(min(x, 100))
}- 直接将
ages['Age']传进cut_off_at_100中不太行,此时我们就可以使用:
apply
- python的pandas库中提供了
apply函数(r中apply是原生函数)。其可以传入数组与函数,然后对每一列的每个元素调用函数,并形成一个包含返回值的新数组。 - 比如上面的例子:
ages['Cut Off Age']=ages['Age'].apply(cut_off_at_100)ages$'Cut Off Age' <- sapply(ages$Age,cut_off_at_100)| Person | Age | Cut Off Age |
|---|---|---|
| A | 17 | 17 |
| B | 117 | 100 |
| C | 52 | 52 |
| D | 100 | 100 |
| E | 6 | 6 |
| F | 101 | 100 |
R语言apply家族
补充R语言apply相关函数及其用法:
apply:用于矩阵或数组(至少二维)- 格式:
apply(X, MARGIN, FUN) X表示矩阵或数组,MARGIN取1表示按行,取2表示按列,FUN表示作用函数
- 格式:
lapply:逐元素处理- 格式:
lapply(X, FUN) - 返回一个列表
- 格式:
sapply- 格式:
sapply(X, FUN) - 与
lapply类似,将结果简化为向量/矩阵
- 格式:
vapply- 格式:
vapply(X, FUN, FUN.VALUE) - 用
FUN.VALUE指定输出类型(比sapply更稳定)
- 格式:
tapply- 格式:
tapply(X, INDEX, FUN) - 按
INDEX将X分组作用函数
- 格式:
mapply- 格式:
mapply(FUN, ...) - 多变量映射(类似python的
map)
- 格式:
实例:身高预测
- 下面我们阐述一个经典的例子:利用预测子女的身高。原始数据(前10行)如下:
| family | father | mother | midparentHeight | children | childNum | gender | childHeight |
|---|---|---|---|---|---|---|---|
| 1 | 78.5 | 67 | 75.43 | 4 | 1 | male | 73.2 |
| 1 | 78.5 | 67 | 75.43 | 4 | 2 | female | 69.2 |
| 1 | 78.5 | 67 | 75.43 | 4 | 3 | female | 69 |
| 1 | 78.5 | 67 | 75.43 | 4 | 4 | female | 69 |
| 2 | 75.5 | 66.5 | 73.66 | 4 | 1 | male | 73.5 |
| 2 | 75.5 | 66.5 | 73.66 | 4 | 2 | male | 72.5 |
| 2 | 75.5 | 66.5 | 73.66 | 4 | 3 | female | 65.5 |
| 2 | 75.5 | 66.5 | 73.66 | 4 | 4 | female | 65.5 |
| 3 | 75 | 64 | 72.06 | 2 | 1 | male | 71 |
| 3 | 75 | 64 | 72.06 | 2 | 2 | female | 68 |
- 我们提取出
father,mother和childHeight这三列,并计算父母身高的平均值,最终得到两列数据:
import pandas as pd
family_heights = pd.read_csv('heights.csv')
heights = pd.DataFrame({
'Parent Average': (family_heights['father'] + family_heights['mother']) / 2,
'Child': family_heights['childHeight']
})
heightsfamily_heights <- read.csv('heights.csv')
heights <- data.frame(
Parent.Average = (family_heights$father + family_heights$mother) / 2,
Child = family_heights$childHeight
)| Parent Average | childHeight |
|---|---|
| 72.75 | 73.2 |
| 72.75 | 69.2 |
| 72.75 | 69 |
| 72.75 | 69 |
| 71 | 73.5 |
| 71 | 72.5 |
| 71 | 65.5 |
| 71 | 65.5 |
| 69.5 | 71 |
| 69.5 | 68 |
- 用散点图表示:
import matplotlib.pyplot as plt
plt.scatter(heights['Parent Average'], heights['Child'], alpha=0.6, edgecolors='w', s=50)
plt.xlabel('Parent Average Height')
plt.ylabel('Child Height')
plt.show()library(ggplot2)
plot <- ggplot(heights, aes(x = Parent.Average, y = Child)) +
geom_point(alpha = 0.6, shape = 21, fill = "steelblue", color = "white", size = 2.5) +
labs(x = "Parent Average Height", y = "Child Height") +
theme_minimal()
plot

可以看到二者基本呈正相关关系。也可以顺便看看python和r可视化的微妙区别
-
下面我们探索数据预测的方法。一种比较直接的想法是,取预测使用的子女身高附近的所有父母平均身高的平均值作为预测值。
-
具体而言,假设我们需要预测父母平均身高为英寸时子女的身高,那么我们就取父母平均身高在到英寸之间的所有子女的身高,再取均值:
import numpy as np close_to_68 = heights.loc[(heights['Parent Average']>=67.5) & (heights['Parent Average']<=68.5)] np.average(close_to_68['Child'])close_to_68 <- heights[heights$Parent.Average >= 67.5 & heights$Parent.Average <= 68.5, ] mean(close_to_68$Child)得到结果约为
67.61英寸。 -
接下来我们尝试将上述过程包装为一个函数:
def predict_child(p_avg):
"""通过父母平均身高预测子女身高。
"""
close_points = heights.loc[(heights['Parent Average']>=p_avg - 0.5) & (heights['Parent Average']<=p_avg + 0.5)]
return np.average(close_points['Child'])
predict_child(68) # 67.61 predict_child <- function(p_avg) {
close_points <- heights[heights$Parent.Average >= p_avg - 0.5 &
heights$Parent.Average <= p_avg + 0.5, ]
mean(close_points$Child)
}
predict_child(66) # 66.18 - 为考查预测的准确性,我们将预测值与真实值进行比较:
heights['Prediction']=heights['Parent Average'].apply(predict_child)
plt.scatter(heights['Parent Average'], heights['Child'], alpha=0.6, edgecolors='w', s=50)
plt.scatter(heights['Parent Average'], heights['Prediction'], alpha=0.6, edgecolors='w', s=50)
plt.xlabel('Parent Average Height')
plt.ylabel('Child Height')
plt.show()heights$Prediction <- sapply(heights$Parent.Average, predict_child)
ggplot(heights, aes(x = Parent.Average, y = Prediction)) +
geom_point(alpha = 0.6,
color = "white",
fill = "gray",
size = 2,
shape = 21) +
geom_point(alpha = 0.6, color = "white", fill = "gray", size = 2, shape = 21) +
labs(x = "Parent Average Height", y = "Child Height") +
theme_minimal() 结果:


注意到,预测的结果近似为一条直线,它也被称为回归线(regression line)。
- 实际上,以上研究正是回归分析的起源——由19世纪英国统计学家高尔顿(达尔文的表弟)发现。另外,这一研究也催生出均值回归(mean reversion)理论。
