近视的茶壶 · 如何使用铁锈中的env_logger机箱在I ...· 2 月前 · |
喝醉的小笼包 · 在Rockchip RK3399 ...· 5 月前 · |
另类的煎饼果子 · 我晒干了沉默,后悔的很冲动:el-table ...· 11 月前 · |
销魂的开心果 · 重磅!清华基于石墨烯的机器学习辅助人机界面的 ...· 1 年前 · |
叛逆的水煮鱼 · map获取第一个key - OSCHINA ...· 1 年前 · |
cppFunction()
转换简单的C++函数—Fibnacci例子
sourceCpp()
转换C++程序—正负交替迭代例子
sourceCpp()
转换C++源文件中的程序—正负交替迭代例子
sourceCpp()
转换C++源程序文件—卷积例子
wrap()
把C++变量返回到R中
as()
函数把R变量转换为C++类型
as()
和
wrap()
的隐含调用
clone
函数
kable()
函数制作表格
## Rows: 34 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): sex, type
## dbl (4): id, age, v0, v1
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
n =
sum
(
!
is.na
(age)),
mean.age=
mean
(age,
na.rm=
TRUE
),
sd.age=
sd
(age,
na.rm=
TRUE
))
|>
knitr
::
kable
()
mean.age
sd.age
mean()
,
median()
。
sd()
,
IQR()
,
mad()
。
min()
,
max()
,
quantile()
。
first(x)
取出
x[1]
,
last(x)
取出
x
的最后一个元素,
nth(x,2)
取出
x[2]
。
可以提供一个缺省值以防某个下标位置不存在。
n()
给出某个组的观测数,
sum(!is.na(x))
统计
x
的非缺失值个数,
n_distinct(x)
统计
x
的不同值个数(缺失值也算一个值),
也可以计算多个变量的不同组合值个数。
count(x)
给出
x
的每个不同值的个数(类似于
table()
函数),
但
count(x)
只能单独作为管道运算的一个步骤,
不能用在
summarise
函数中。
这里有些函数是dplyr包提供的, 仅适用于tibble类型。
count()
函数允许用
wt
添加一个权重,
实际是重复观测数。
有如下的汇总数据:
其中的
is.numeric
用来筛选需要进行统计的变量子集,
可以替换成其它的示性函数(对每列返回一个逻辑值的函数),
如
is.character
,
is.Date
,
is.POSIXct
,
is.logical
,
is.factor
。
为了对所有列都计算相同的统计量,
可以在
across()
中指定变量集合为
everything()
,如
d.cancer |> select(v0, v1) |> summarise(across(everything(), list(avg = \(x) mean(x, na.rm=TRUE), std = \(x) sd(x, na.rm=TRUE))) ) |> knitr::kable(digits=2)
## # A tibble: 4 × 7
## id a b c a_na b_na c_na
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 5 10 6 5 10 6
## 2 2 -999 12 8 NA 12 8
## 3 3 4 -999 -999 4 NA NA
## 4 4 7 9 10 7 9 10
其中
.names
的取值是一个模式字符串,
模式中的
{.col}
特指原始变量名。
下面的函数
expand_dates()
,
输入一个数据框,
选择其中的所有日期列,
将其每一个都拆分为年、月、日:
## The following objects are masked from 'package:base':
## date, intersect, setdiff, union
expand_dates <- function(df){ mutate(across( where(is.Date), list(year = year, month = month, day = mday)
filter(if_any(a_na:c_na, is.na))d.dates <- read_csv( 'data/dates.csv', locale=locale(encoding="GBK"), col_types=cols( `序号`=col_integer(), `出生日期`=col_date(format="%Y/%m/%d"), `发病日期`=col_date(format="%Y/%m/%d") knitr::kable(d.dates[1:4,])
## # A tibble: 2 × 7
## id a b c a_na b_na c_na
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2 -999 12 8 NA 12 8
## 2 3 4 -999 -999 4 NA NA
## # A tibble: 2 × 7
## id a b c a_na b_na c_na
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 5 10 6 5 10 6
## 2 4 7 9 10 7 9 10
数据汇总问题更常见的是作分组汇总。
dplyr包的
group_by()
函数对数据框(或tibble)分组,
随后的
summarise()
将按照分组汇总。
group_by()
对数据框增加分组信息,
使得后续的管道操作针对每一组内进行操作,
但
group_by()
并不修改数据框内容。
d.cancer |> group_by(sex) |> summarise( count = n(), mean.age = mean(age, na.rm=TRUE)) |> knitr::kable(digits=2)
上面的程序应该将
v0_count
和
v1_count
合并,
但是
summarise
在有多个变量和多个统计量时总会将变量两两搭配,
确实需要时可以分别统计再合并,结果程序会略繁琐一些:
bind_cols( d.cancer |> group_by(sex) |> summarise(count = n() ), d.cancer |> group_by(sex) |> summarise( across(c("v0", "v1"), list( avg = \(x) mean(x, na.rm=TRUE), std = \(x) sd(x, na.rm=TRUE)))) |> select(-sex) knitr::kable(digits=2)
## `summarise()` has grouped output by 'sex'. You can override using the `.groups`
## argument.
summarise
(
ntotal=
sum
(freq))
|>
knitr
::
kable
()
## `summarise()` has grouped output by 'sex'. You can override using the `.groups`
## argument.
ntotal
而是按原来交叉分类的外层分类即性别分组计算了总人数。
这是因为交叉分组计算频数后的结果仍按照外层分类变量sex分组,
所以
summarise(ntotal=sum(freq))
也是分两组进行的。
在第一个
summarise()
中加
.groups = "drop"
可以不保留分组信息,如:
d.cancer |> group_by(sex, type) |> summarise(freq=n(), .groups="drop") |> summarise(ntotal=sum(freq)) |> knitr::kable()
## `summarise()` has grouped output by 'sex'. You can override using the `.groups`
## argument.
ntotal
dplyr和tidyr关于变量和变量集合使用两种写法类似、底层不同的做法,
即数据掩码(data masking)和整洁选择(tidy select)。
mutate()
,
filter()
,
arrange()
,
count()
,
group_by()
,
summarise()
在底层使用数据掩码方法。
select()
,
relocate()
,
rename()
,
pull()
,
across()
在底层使用整洁选择方法。
在使用数据掩码方法的函数中,
如果针对的数据框变量是原样(不是字符串或者字符型变量)输入的,
可以用
{{ var_names }}
的写法表示。
输入一个分组变量和一个分析变量,
计算均值和最大值:
group_summ_1 <- function(df, group_var, ana_var){ group_by({{ group_var }}) |> summarise( mean = mean({{ana_var}}, na.rm=TRUE), max = max({{ana_var}}, na.rm=TRUE)) group_summ_1(d.class, sex, height)
## # A tibble: 2 × 3
## sex mean max
## <fct> <dbl> <dbl>
## 1 M 63.9 72
## 2 F 60.6 66.5
上面的
group_by
和
summarise
都是数据掩码方式指定变量的,
所以当调用自定义函数时如果原样写分组变量和分析变量名,
而不是写成字符串或者输入为字符型变量,
就只要用
{{ var_name }}
这种格式。
但是,如果输入为字符串则不行:
group_summ_1(d.class, "sex", "height") ## # A tibble: 1 × 3 ## `"sex"` mean max ## <chr> <dbl> <chr> ## 1 sex NA height
.data[[var_name]]
这种方式访问,如:
group_summ_2 <- function(df, group_var, ana_var){ group_by(.data[[group_var]]) |> summarise( mean = mean(.data[[ana_var]], na.rm=TRUE), max = max(.data[[ana_var]], na.rm=TRUE)) group_summ_2(d.class, "sex", "height")
## # A tibble: 2 × 3
## sex mean max
## <fct> <dbl> <dbl>
## 1 M 63.9 72
## 2 F 60.6 66.5
基于数据掩码的函数在指定多个变量时,
为了能够使用整洁选择时可用的方法,
需要用
pick()
函数进行说明。
group_by()
是基于数据掩码的,
如果要指定多个变量,
无法直接使用如
c(sex, age)
这样的方法,
可以在自定义函数中写成
pick({{ group_var }})
,
group_summ_3 <- function(df, group_var, ana_var){ group_by(pick({{ group_var }})) |> summarise( mean = mean({{ ana_var }}, na.rm=TRUE), max = max({{ ana_var }}, na.rm=TRUE)) group_summ_3(d.class, c(sex, age), height)
select
,
across
等函数在指定变量时,
使用整洁选择方式。
如果变量是用整洁选择函数或者原样变量名方式输入的,
仍可以用
{{ var_names }}
这种格式。
group_summ_4 <- function(df, group_var, ana_vars){ group_by({{ group_var }}) |> summarise(across( {{ ana_vars }}, list(avg = ~ mean(., na.rm=TRUE), max = ~ max(., na.rm=TRUE)))) group_summ_4(d.class, sex, c(height, weight))
## # A tibble: 2 × 5
## sex height_avg height_max weight_avg weight_max
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 M 63.9 72 109. 150
## 2 F 60.6 66.5 90.1 112.
其中的
group_by
是基于数据掩码的,
across
是基于整洁选择的。
在
across
中如果输入字符型变量,
则不能用
{{ var_names }}
这种方法,
而是用
all_of(var_names)
,
group_summ_5 <- function(df, group_var, ana_vars){ group_by({{ group_var }}) |> summarise(across( all_of(ana_vars), list(avg = ~ mean(., na.rm=TRUE), max = ~ max(., na.rm=TRUE)))) ana_vars <- c("height", "weight") group_summ_5(d.class, sex, ana_vars)
## # A tibble: 2 × 5
## sex height_avg height_max weight_avg weight_max
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 M 63.9 72 109. 150
## 2 F 60.6 66.5 90.1 112.
在自定义函数中使用dplyr功能, 可参考 Programming with dplyr 。 在自定义函数中使用tidyr功能, 可参考 programming with tidyr 。
nest
和
unnest
dplyr包的
group_by
与
summarise
、
summarise_at
等函数配合,
可以对数据框分组计算各种概括统计量。
但是,如果分组以后希望进行更复杂的统计分析,
比如分组回归建模,
summarise
就不够用了。
可以用基本R的
split
函数将数据框按某个分类变量拆分为子数据框的列表,
然后用purrr包的map类函数分类建模,
最终将各个模型的结果合并为一个数据框。
上面的办法虽然可行,
但是管理不够方便。
tidyr包(属于tidyverse系列,载入tidyverse时会自动载入)提供了
nest
和
unnest
函数,
可以将子数据框保存在tibble中,
可以将保存在tibble中的子数据框合并为一个大数据框。
tibble允许存在数据类型是列表(list)的列,
子数据框就是以列表数据类型保存在tibble的一列中的。
group_by
与
nest
配合
对数据框用
group_by
分组后调用
nest
函数就可以生成每个组的子数据框。
type
分类拆分为2个子数据框,
存入tibble的data列中:
## # A tibble: 2 × 2
## # Groups: type [2]
## type data
## <chr> <list>
## 1 腺癌 <tibble [12 × 5]>
## 2 鳞癌 <tibble [22 × 5]>
现在
data
列是列表类型的,
有2个元素,
每个元素是一个子数据框。
group_by()
中也可以用两个或多个分类变量构成交叉分组。
可以用purrr包的
map()
等函数对每个子数据框进行处理,
结果可以用
mutate
保存为新的列表类型的列,
如果结果是数值型标量也可以保存为普通的数据框列。
例如,下面先定义对子数据框回归建模的函数,
然后用purrr包的
map
函数将回归建模的函数作用到
data
列的每个元素,
用
mutate
保存到列表类型的
lmr
列中:
fmod <- function(subdf) lm(v1 ~ v0, data = subdf) mod.cancer <- d.cancer |> group_by(type) |> nest() |> mutate(lmr = map(data, fmod)) mod.cancer
## # A tibble: 2 × 3
## # Groups: type [2]
## type data lmr
## <chr> <list> <list>
## 1 腺癌 <tibble [12 × 5]> <lm>
## 2 鳞癌 <tibble [22 × 5]> <lm>
写一个函数从一个回归模型及相应子数据框中, 提取R方, 将提取的结果保存为普通数值型列r.squared:
frsqr <- function(mod){ summary(mod)$r.squared mod.cancer |> mutate( r.squared = map_dbl(lmr, frsqr)) |> select(-data, -lmr)
## # A tibble: 2 × 2
## # Groups: type [2]
## type r.squared
## <chr> <dbl>
## 1 腺癌 0.710
## 2 鳞癌 0.520
map()
和
map_dbl()
中输入函数时可以用purrr包的无名函数写法,如:
d.cancer |> group_by(type) |> nest() |> mutate( lmr = map(data, ~ lm(v1 ~ v0, data = .x)), r.squared = map_dbl(lmr, ~ summary(.x)$r.squared)) |> select(-data, -lmr)
## # A tibble: 2 × 2
## # Groups: type [2]
## type r.squared
## <chr> <dbl>
## 1 腺癌 0.710
## 2 鳞癌 0.520
也可以从每个模型提取多个值, 为了使得多个值在展开时能保存在同一行中, 需要将每个子数据框的提取结果保存为一个一行的子数据框:
fextract <- function(mod){ x1 <- coef(mod) tibble( intercept = x1[1], v0 = x1[2], r.squared = summary(mod)$r.squared mod.cancer |> mutate( outlist = map(lmr, fextract))
## # A tibble: 2 × 4
## # Groups: type [2]
## type data lmr outlist
## <chr> <list> <list> <list>
## 1 腺癌 <tibble [12 × 5]> <lm> <tibble [1 × 3]>
## 2 鳞癌 <tibble [22 × 5]> <lm> <tibble [1 × 3]>
结果的
outlist
列是列表类型的,
每个元素是一个
\(1 \times 3\)
的tibble。
下面,就可以用
unnest
将每个组提取的回归结果转换为普通的数据框列:
## # A tibble: 2 × 4
## # Groups: type [2]
## type intercept v0 r.squared
## <chr> <dbl> <dbl> <dbl>
## 1 腺癌 0.225 0.370 0.710
## 2 鳞癌 5.51 0.374 0.520
提取的结果也可以是一个不止一行的子数据框,例如, 提取回归结果中的系数估计、标准误差、t统计量和检验p值组成的矩阵:
fcoefmat <- function(mod){ as_tibble(summary(mod)$coefficients, rownames="term") mod.cancer |> mutate( outlist = map(lmr, fcoefmat)) |> select(-data, - lmr) |> unnest(outlist)
## # A tibble: 4 × 6
## # Groups: type [2]
## type term Estimate `Std. Error` `t value` `Pr(>|t|)`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 腺癌 (Intercept) 0.225 10.2 0.0221 0.983
## 2 腺癌 v0 0.370 0.0749 4.94 0.000585
## 3 鳞癌 (Intercept) 5.51 10.8 0.510 0.616
## 4 鳞癌 v0 0.374 0.0803 4.66 0.000151
为了更好地提取统计模型的信息为规整的数据框格式, broom扩展包提供了tidy函数, 可以将统计模型的输出转换为数据框; 这些功能与tidyr的nest, unnest配合, 可以很好地提取统计模型的信息,如:
## # A tibble: 4 × 6
## # Groups: type [2]
## type term estimate std.error statistic p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 腺癌 (Intercept) 0.225 10.2 0.0221 0.983
## 2 腺癌 v0 0.370 0.0749 4.94 0.000585
## 3 鳞癌 (Intercept) 5.51 10.8 0.510 0.616
## 4 鳞癌 v0 0.374 0.0803 4.66 0.000151
unnest
提取出的信息也可以是一个向量,
在展开时会展开到同一列中。
对每个组提取回归的拟合值:
## # A tibble: 34 × 7
## # Groups: type [2]
## type id age sex v0 v1 v1hat
## <chr> <dbl> <dbl> <chr> <dbl> <dbl> <dbl>
## 1 腺癌 1 70 F 26.5 2.91 10.0
## 2 腺癌 2 70 F 135. 35.1 50.4
## 3 腺癌 3 69 F 210. 74.4 77.9
## 4 腺癌 4 68 M 61 35.0 22.8
## 5 腺癌 6 75 F 330. 112. 123.
## 6 腺癌 11 55 M 125. 12.3 46.6
## 7 腺癌 13 55 F 12.9 2.3 5.01
## 8 腺癌 14 75 M 40.2 24.0 15.1
## 9 腺癌 15 61 F 12.6 7.39 4.88
## 10 腺癌 19 NA F 32.9 9.45 12.4
## # … with 24 more rows
程序中的
unnest
将
data
列和
v1hat
列都释放为普通的数据框列了,
data
列中释放出了多列原始数据,
fitted
列中释放出了v1回归拟合值。
summarise
统计量用列表表示
summarise
等函数如果将结果用
list()
声明,
汇总结果就可以保存为列表类型的列,
结果可以包含多个值,
unnest
可以将结果恢复成正常的数据框,
vnames <- expand_grid( var = c("v0", "v1"), stat = c("min", "max")) |> pmap_chr(paste, sep="_") d.cancer |> group_by(type) |> summarise( stat = list(vnames), value = list(c(range(v0), range(v1))) ) |> unnest(c(stat, value))
## # A tibble: 8 × 3
## type stat value
## <chr> <chr> <dbl>
## 1 鳞癌 v0_min 13.2
## 2 鳞癌 v0_max 238.
## 3 鳞癌 v1_min 3.34
## 4 鳞癌 v1_max 128.
## 5 腺癌 v0_min 12.6
## 6 腺癌 v0_max 330.
## 7 腺癌 v1_min 2.3
## 8 腺癌 v1_max 122.
这个例子可以用长宽表转换方法变成每个统计量占一列:
d.cancer |> group_by(type) |> summarise( stat = list(vnames), value = list(c(range(v0), range(v1))) ) |> unnest(c(stat, value)) |> separate(stat, into = c("variable", "stat"), sep="_") |> pivot_wider( names_from = "stat", values_from = "value"
## # A tibble: 4 × 4 ## type variable min max ## <chr> <chr> <dbl> <dbl> ## 1 鳞癌 v0 13.2 238. ## 2 鳞癌 v1 3.34 128. ## 3 腺癌 v0 12.6 330. ## 4 腺癌 v1 2.3 122.
24.6.4
unnest
的语法格式
unnest()
第一自变量为管道输入的数据框, 第二自变量cols
可以用如下格式:单个变量名,不需要写成字符串形式; 多个变量名,写成 c(x, y, z)
这样的格式,不需要写成字符型向量;保存在字符型向量中的变量名,用 one_of(vnames)
格式, 其中vnames
是保存了要释放的列名的字符型向量的变量名; 也可以写成one_of(c("x", "y", "z"))
这样的格式。24.6.5 直接生成列表类型的列
也可以直接生成列表类型的列, 符合条件时可以用
unnest()
合并为大数据框。d1 <- tibble( id = 1:2, df = vector("list", length=2)) d1[["df"]][1] <- list( tibble(x=1, y=2) d1[["df"]][2] <- list( tibble(x=11:12, y=21:22) unnest(cols = c(df))
## # A tibble: 3 × 3 ## id x y ## <int> <dbl> <dbl> ## 1 1 1 2 ## 2 2 11 21 ## 3 2 12 22
24.7 数据分析流程控制
一个完整的数据分析过程, 经常需要反复地修改程序, 更新数据。 如果每次都重新运行整个的数据整理、概括、建模、结果呈现的代码, 会花费很多时间, 中间某一步骤出错时如果修改程序重新运行, 就使得前面步骤白作, 如果人为控制重新运行哪些代码, 容易犯错使得利用了旧的结果。
解决这样的问题的工具是make类的软件, 这些软件规定各个任务之间的依赖关系, 当任务依赖的数据和代码更新时自动管理需要重新运行哪些程序。 R提供了targets包可以实现这样的任务管理功能, 规定了数据、代码、结果的依赖关系后可以仅运行受到影响的部分。
https://docs.ropensci.org/targets/。24.8 基本R的汇总功能
24.8.1
summary()
函数对数值型向量
x
,用summary(x)
可以获得变量的平均值、中位数、 最小值、最大值、四分之一和四分之三分位数。 如## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 12.58 43.77 93.40 110.08 157.18 330.24
## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 2.30 12.73 30.62 44.69 72.94 128.34
v0是放疗前的肿瘤体积, v1是放疗后的体积, 可以看出放疗后体积减小了很多。
可以用盒形图表现类似的信息,如
对一个数据框
d
, 用summary(d)
可以获得每个连续型变量的基本统计量, 和每个离散取值变量的频率。如## id age sex type ## Min. : 1.00 Min. :49.00 Length:34 Length:34 ## 1st Qu.: 9.25 1st Qu.:55.00 Class :character Class :character ## Median :17.50 Median :67.00 Mode :character Mode :character ## Mean :17.50 Mean :64.13 ## 3rd Qu.:25.75 3rd Qu.:70.00 ## Max. :34.00 Max. :79.00 ## NA's :11 ## v0 v1 ## Min. : 12.58 Min. : 2.30 ## 1st Qu.: 43.77 1st Qu.: 12.73 ## Median : 93.40 Median : 30.62 ## Mean :110.08 Mean : 44.69 ## 3rd Qu.:157.18 3rd Qu.: 72.94 ## Max. :330.24 Max. :128.34
对数据框
d
,用str(d)
可以获得各个变量的类型和取值样例。 如
## spec_tbl_df [34 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame) ## $ id : num [1:34] 1 2 3 4 5 6 7 8 9 10 ... ## $ age : num [1:34] 70 70 69 68 67 75 52 71 68 79 ... ## $ sex : chr [1:34] "F" "F" "F" "M" ... ## $ type: chr [1:34] "腺癌" "腺癌" "腺癌" "腺癌" ... ## $ v0 : num [1:34] 26.5 135.5 209.7 61 237.8 ... ## $ v1 : num [1:34] 2.91 35.08 74.44 34.97 128.34 ... ## - attr(*, "spec")= ## .. cols( ## .. id = col_double(), ## .. age = col_double(), ## .. sex = col_character(), ## .. type = col_character(), ## .. v0 = col_double(), ## .. v1 = col_double() ## .. ) ## - attr(*, "problems")=<externalptr>
用
head(d)
可以返回数据框(或向量、矩阵)的前几行, 用tail(d)
可以返回数据框的后几行。24.8.2 连续型变量概括函数
对连续取值的变量
x
, 可以用mean
,std
,var
,sum
,prod
,min
,max
等函数获取基本统计量。 加na.rm=TRUE
选项可以仅对非缺失值计算。
sort(x)
返回排序后的结果。rev(x)
把x
所有元素次序颠倒后返回。quantile(x, c(0.05, 0.95))
可以求x
的样本分位数。rank(x)
对x
求秩得分(即名次,但从最小到最大排列)。24.8.3 分类变量概括
分类变量一般输入为因子。 对因子或其它向量
## F M ## 13 21x
,table(x)
返回x
的每个不同值的频率(出现次数), 结果为一个类(class)为table的一维数组。 每个元素有对应的元素名,为x
的各水平值。## F ## 13
对单个分类变量,
table
结果是一个有元素名的向量。 用as.data.frame()
函数把table
的结果转为数据框:## Var1 Freq ## 1 F 13 ## 2 M 21
用
## F M ## 0.3823529 0.6176471prop.table()
将频数转换成百分比:
table
作的单变量频数表可以用
barplot
表现为图形,如:
对两个分类变量
x1
和
x2
,
其每个组合的出现次数可以用
table(x1,x2)
函数统计, 结果叫做列联表。
## type
## sex 鳞癌 腺癌
## F 4 9
## M 18 3
结果是一个类为table的二维数组(矩阵),
每行以第一个变量
x1
的各水平值为行名,
每列以第二个变量
x2
的各水平值为列名。
这里用了
with()
函数引入一个数据框,
后续的参数中的表达式可以直接使用数据框的变量。
对两个分类变量,
table
结果是一个矩阵。
用
as.data.frame
函数把
table
的结果转为数据框:
## sex type Freq
## 1 F 鳞癌 4
## 2 M 鳞癌 18
## 3 F 腺癌 9
## 4 M 腺癌 3
列联表的结果可以用条形图表示。如
对于
table()
的结果列联表,
可以用
addmargins()
函数增加行和与列和。 如
## type
## sex 鳞癌 腺癌 Sum
## F 4 9 13
## M 18 3 21
## Sum 22 12 34
用
margin.table()
可以计算列联表行或列的和并返回,如
## sex
## F M
## 13 21
## type
## 鳞癌 腺癌
## 22 12
用
prop.table(r)
把一个列联表
r
转换成百分比表。 如
## type
## sex 鳞癌 腺癌
## F 0.11764706 0.26470588
## M 0.52941176 0.08823529
用
prop.table(res,1)
把列联表
res
转换成行百分比表。
用
prop.table(res,2)
把列联表
res
转换成列百分比表。 如
## type
## sex 鳞癌 腺癌
## F 0.3076923 0.6923077
## M 0.8571429 0.1428571
## type
## sex 鳞癌 腺癌
## F 0.1818182 0.7500000
## M 0.8181818 0.2500000
在有多个分类变量时,
用
as.data.frame(table(x1, x2, x3))
形成多个分类变量交叉分类的频数统计数据框。
dplyr包的
count()
功能与
table()
类似。
用
colMeans()
对数据框或矩阵的每列计算均值,
用
colSums()
对数据框或矩阵的每列计算总和。
用
rowMeans()
和
rowSums()
对矩阵的每行计算均值或总和。
数据框与矩阵有区别,
某些适用于矩阵的计算对数据框不适用,
例如矩阵乘法。
用
as.matrix()
把数据框的数值子集转换成矩阵。
对矩阵,用
apply(x, 1, FUN)
对矩阵x的每一行使用函数FUN计算结果,
用
apply(x, 2, FUN)
对矩阵x的每一列使用函数FUN计算结果。
如果
apply(x,1,FUN)
中的FUN对每个行变量得到多个
\(m\)
结果,
结果将是一个矩阵,行数为
\(m\)
,列数等于nrow(x)。
如果
apply(x,2,FUN)
中的FUN对每个列变量得到多个
\(m\)
结果,
结果将是一个矩阵,行数为
\(m\)
,列数等于ncol(x)。
apply(as.matrix(iris[,1:4]), 2, function(x) c(n=sum(!is.na(x)), mean=mean(x, na.rm=TRUE), sd=sd(x, na.rm=TRUE)))
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## n 150.0000000 150.0000000 150.000000 150.0000000
## mean 5.8433333 3.0573333 3.758000 1.1993333
## sd 0.8280661 0.4358663 1.765298 0.7622377
tapply()
分组汇总
用
tapply()
函数进行分组概括, 格式为:
其中
X
是一个向量,
INDEX
是一个分类变量,
FUN
是概括统计函数。
比如,下面的程序分性别组计算疗前体积的均值:
## F M
## 113.2354 108.1214
aggregate()
分组概括数据框
aggregate
函数对输入的数据框用指定的分组变量(或交叉分组)
分组进行概括统计。
例如,下面的程序按性别分组计算年龄、疗前体积、疗后体积的平均值:
## sex age v0 v1
## 1 F 66.14286 113.2354 42.65538
## 2 M 63.25000 108.1214 45.95524
aggregate()
第一个参数是数据框,
第二个参数是列表,列表元素是用来分组或交叉分组的变量,
第三个参数是概括用的函数,
概括用的函数的选项可以在后面给出。
可以交叉分组后概括,如
## sex type v0 v1
## 1 F 鳞癌 126.99250 45.54750
## 2 M 鳞癌 113.55722 49.65556
## 3 F 腺癌 107.12111 41.37000
## 4 M 腺癌 75.50667 23.75333
split()
函数分组后概括
split函数可以把数据框的各行按照一个或几个分组变量分为子集的列表,
然后可以用
sapply()
或
vapply()
对每组进行概括。
## F M
## v0 113.23538 108.12143
## v1 42.65538 45.95524
返回矩阵,行为变量v0, v1,列为不同性别,
值为相应的变量在各性别组的平均值。
当
sapply()
对列表每项的操作返回一个向量时,
总是列表每项的输出保存为结果的一列。
colMeans
函数计算分组后数据框子集每列的平均值。
plyr是一个专注于分组后分别分析然后将分析结果尽可能合理地合并的扩展包, 功能强大, dplyr包仅针对数据框,使用更方便,但是对于复杂情况功能不如plyr包强。 plyr包已经被dplyr、purrr包代替,不再继续开发新版本。 这部分内容仅作为备忘, 读者可以跳过。
plyr的输入支持数组、数据框、列表, 输出支持数组、数据框、列表或无输出。 分组分析的函数输出格式需要与指定的输出格式一致。
这里主要介绍从数据框分组概括并将结果保存为数据框的方法,
使用plyr包的
ddply()
函数。
实际上,dplyr包的这种功能更方便。
plyr包的优点是可以自定义概括函数,
使得结果表格符合用户的预期,
处理多个变量时程序更简洁。
plyr包与dplyr包的函数名冲突比较大, 所以需要先卸载dplyr包再调用plyr包:
ddply()
函数第一自变量是要分组的数据框,
第二自变量是分组用的变量名,
第三自变量是一个概括函数,
此概括函数以一个数据框子集(数据类型是数据框)为输入,
输出是一个数值、一个数值型向量或者一个数据框,
但最好是数据框。
例如,按性别分组,计算v0的平均值:
下面的程序按性别分组, 分别计算v0与v1的平均值:
下面的程序按性别分组,计算v0和v1的平均值、标准差:
f1 <- function(dsub){ tab <- tibble( "变量"=c("v0", "v1"), "均值"=c(mean(dsub[,"v0"], na.rm=TRUE), mean(dsub[,"v1"], na.rm=TRUE)), "标准差"=c(sd(dsub[,"v0"], na.rm=TRUE), sd(dsub[,"v1"], na.rm=TRUE))) ddply(d.cancer, "sex", f1)
注意
f1()
结果是一个数据框。
程序有些重复内容,对每个变量和每个统计量都需要分别写出,
如果这样用plyr包就不如直接用
dplyr::summarise()
了。
下面用
vapply()
简化程序。
按性别分组,然后v0、v1各自一行结果, 计算非缺失值个数、均值、标准差、中位数:
f2 <- function(d, variables){ d1 <- d[,variables,drop=FALSE] nnotmiss <- vapply(d1, function(x) sum(!is.na(x)), 1L) xm <- vapply(d1, mean, 0.0, na.rm=TRUE) xsd <- vapply(d1, sd, 1.0, na.rm=TRUE) xmed <- vapply(d1, median, 0.0, na.rm=TRUE) data.frame(variable=variables, n=nnotmiss, mean=xm, sd=xsd, median=xmed) ddply(d.cancer, "sex", f2, variables = c("v0", "v1"))
f2()
函数针对分组后的数据框子集,
这样的函数可以先在一个子集上试验。
在
f2()
函数中,
设输入的数据子集为
d
,
要分析的变量组成的数据框为
d1
,
用
vapply()
函数对
d1
的每一列计算一种统计量,
然后将每种统计量作为结果数据框的一列。
vapply()
函数类似于
lapply()
和
sapply()
,
但是用第三个自变量表示要应用的变换函数的返回值类型和个数,
用举例的方法给出。
ddply()
也可以对交叉分类后每个类分别汇总,
例如按照性别与病理类型交叉分组后汇总v0、v1:
上面的程序写法适用于已知要分析的变量名的情况。 如果想对每个数值型变量都分析, 而且想把要计算的统计量用统一的格式调用,可以写成:
f3 <- function(d){ ff <- function(x){ c(n=sum(!is.na(x)), each(mean, sd, median)(x, na.rm=TRUE)) ldply(Filter(is.numeric, d), ff) ddply(d.cancer, "sex", f3)
ff()
函数对输入的一个数值型向量计算4种统计量,
返回一个长度为4的数值型向量,
用来对分组后的数据子集中的一列计算4种统计量。
plyr包的
each()
函数接受多个函数,
返回一个函数可以同时得到这几个函数的结果,
结果中各元素用输入的函数名命名。
f3()
函数中的
Filter
函数用于从列表或数据框中取出满足条件的项,
这里取出输入的数据子集
d
中所有的数值型列。
f3()
函数中的
ldply()
函数接受一个列表或看成列表的一个数据框,
对数据框的每列应用
ff()
函数计算4种统计量,
然后合并所有各列的统计量为一个数据框,
结果数据框的每行对应于
d
中的一列。
程序中的
ddply()
函数接受一个数据框,
第二自变量指定用来将数据框分组的变量,
第三自变量
f3()
是对分组后的数据框子集进行分析的函数,
此函数接受一个数据框,输出一个数据框。
上面的程序也可以利用无名函数写成:
f4 <- function(x){ c(n=sum(!is.na(x)), each(mean, sd, median)(x, na.rm=TRUE)) ddply(d.cancer, "sex", function(d) ldply(Filter(is.numeric, d), f4))
把
patients.csv
读入“d.patients”中,
并计算发病年龄、发病年、发病月、
发病年月(格式如“200702”表示2007年2月份)。
把“现住地址国标”作为字符型,去掉最后两位,仅保留前6位数字, 保存到变量“地址编码”中。
按照地址编码和发病年月交叉分类汇总发病人数, 保存到数据框d.pas1中, 然后保存为CSV文件“分区分年月统计.csv”中。 要求结果有三列:“地址编码”、“发病年月”、“发病人数”。
按照地址编码和发病月分类汇总发病人数, 保存到数据框d.pas2中, 然后保存为CSV文件“分区分月统计.csv”中。 要求每个地址编码占一行, 各列为地址编码以及1、2、…………、12各月份, 每行为同一地址编码各月份的发病数。
按发病年月和性别汇总发病人数, 并计算同年月不分性别的发病总人数。 结果保存到数据框d.pas3中, 然后保存到CSV文件“分年月分性别统计.csv”中。 要求每个不同年月占一行, 变量包括年月、男性发病数、女性发病数、总计。
分析病人的职业分布,保存到数据框d.pas4中, 然后保存到CSV文件“职业构成.csv”中。 要求各列为职业、发病人数、百分比(结果乘以100并保留一位小数)。
把年龄分成0—9, 11—19, ……, 70以上各段, 保存为“年龄段”变量。 用年龄段和性别交叉汇总发病人数和百分比(结果乘以100并保留一位小数), 保存到“年龄性别分布.csv”中。 要求将每个年龄段的男性发病人数、发病率、女性发病人数、发病率存为一行。