打算用两篇博客来记录下平时使用R语言进行数据处理、绘图遇见的一些问题。第一篇首先介绍下使用data.frame处理数据的常用操作,第二篇总结下使用ggplot2绘图时常用的一些操作。每篇博客在基础介绍完后总结下自己遇到过的问题,不定时补充。
可以从文件和构造函数创建data.frame。
函数名 | 包含库 |
---|---|
read.csv | - |
read.csv2 | - |
read_excel | readxl |
read_xls | readxl |
read_xlsx | readxl |
read.csv(file, header = TRUE, sep = ",", quote = "\"",
dec = ".", fill = TRUE, comment.char = "", ...)
read.csv2(file, header = TRUE, sep = ";", quote = "\"",
dec = ",", fill = TRUE, comment.char = "", ...)
主要参数包括
file:文件路径。注意使用相对路径时,是相对工作目录,工作目录绝对路径使用getwd()
查看,以及使用setwd()
设置。
header:是否将文件第一行当作行名,默认为TRUE
。选FALSE
时依然会读取第一行,只不过会自动生成默认行名,使用rowname()<-
更改即可。
sep:csv文件的分隔符,默认为",",常见的分隔符包括","、" “、”;"。
quote:字符的表示方式,默认用" “包围引号。
dec:小数点的表示方式,默认为”.";
fill:当各列变量数不一样时是否填补,默认为TRUE
。注意补充的类型与读取文件后,程序判定的列变量类型有关,如果列变量是数字会用NA
,如果是字符则会用""
,不会用NULL
填补也不能用is.null()
检查。
comment.char:指定读取文件中的注释字符,注释字符及其后不会被读取。
library(readxl)
read_excel(path, sheet = NULL, range = NULL, col_names = TRUE,
col_types = NULL, na = "", trim_ws = TRUE, skip = 0,
n_max = Inf, guess_max = min(1000, n_max), progress = readxl_progress(),
.name_repair = "unique")
read_xls(path, sheet = NULL, range = NULL, col_names = TRUE,
col_types = NULL, na = "", trim_ws = TRUE, skip = 0,
n_max = Inf, guess_max = min(1000, n_max), progress = readxl_progress(),
.name_repair = "unique")
read_xlsx(path, sheet = NULL, range = NULL, col_names = TRUE,
col_types = NULL, na = "", trim_ws = TRUE, skip = 0,
n_max = Inf, guess_max = min(1000, n_max), progress = readxl_progress(),
.name_repair = "unique")
这里给个自己R读取excel所有工作表的写法,
file = 'data.xlsx'
sheets <- excel_sheets(file)
data <- data.frame()
i <- 1
for (time in sheets) {
data_once <- read_xlsx(file,
sheet=time,
col_names=F,
# range = ,根据数据需要和效率确定读取范围
# 参数值见cell-specification: Specify cells for reading
)
data.once$x <- seq(1, length(rownames(data_once))
data = rbind(data, data.once)
print(i)
i = i + 1
}
time <- 1:3
value1 <- c(1, 2, 2)
value2 <- c(2, 0, 2)
data <- data.frame(time, value1, value2)
创建的data.frame data为
time value1 value2
1 1 1 2
2 2 2 0
3 3 2 2
当需要创建具有一定规则的向量时,除了使用循环还可以使用以下几个辅助函数:
使用:
创建连续数字的序列,
vector.1 <- 1:5
# 1 2 3 4 5
当然也可以使用小数,但是创建的序列步长为1。
vector.2 <- 0.1 2.2
# 0.1 1.1 2.1
更改步长可以用seq(from, to, step)
,
vector.3 <- seq(0.1, 2.2, 0.5)
# 0.1 0.6 1.1 1.6 2.1
有时候可能需要创建按规则重复的向量,使用rep(vector, repeat_times)
vector.4 <- rep(1:3, 2)
# 1 2 3 1 2 3
从辅助函数得到的临时变量创建data.frame时就可以指定列名,不用创建后再更改
data.frame("serial" = 1:4, "value" = rep(c(1, 0), 2))
# serial value
# 1 1 1
# 2 2 0
# 3 3 1
# 4 4 0
以前述data为例。
> data
time value1 value2
1 1 1 2
2 2 2 0
3 3 2 2
访问行、列、指定行列数据都可以从索引或行名、列名访问
# 从索引访问第2行
data[2,]
# time value1 value2
# 2 2 2 0
# 从列名访问列
data$value1
# 1 2 2
# 从行名访问行
data["3", ]
# time value1 value2
# 3 3 2 2
# 访问指定的其他行列
# 只能通过索引操作,不能从行名、列名操作,行名、列名都是字符类型,不支持-操作符,如data[-"2",]、data[,-"value1"]
data[-2,]
# time value1 value2
# 1 1 1 2
# 3 3 2 2
获取行名、列名
rownames(data)
# "1" "2" "3"
colnames(data)
# "time" "value1" "value2"
# 通过赋值可以对行名、列名进行更改
常用的筛选包括:
<
、>
、!=
、==
|
或&
。注意别写成||
、&&
%in%
以data.2
为例。
data.2 <- data.frame("time" = rep(1:3, 2),
"value" = c(data$value1, data$value2),
"type" = gl(2, 3, labels = c("value1", "value2")))
> data.2
time value type
1 1 1 value1
2 2 2 value1
3 3 2 value1
4 1 2 value2
5 2 0 value2
6 3 2 value2
筛选value = 0的数据。需要注意筛选操作获得的是基于行或列的一系列布尔值,别忘了","。
data.2[data.2$value == 0, ] # 注意别忘了","
# time value type
# 5 2 0 value2
筛选value值在向量[0, 1]中的数据
data.2[data.2$value %in% c(0, 1), ]
# time value type
# 1 1 1 value1
# 5 2 0 value2
筛选time < 3且time > 1的数据
data.2[data.2$time > 1 & data.2$time < 3, ]
# time value type
# 2 2 2 value1
# 5 2 0 value2
基于上述访问、筛选操作后重新赋值即可实现删除操作。
data.remove_row_2 <- data.2
data.remove_row_2 <- data.remove_row_2[-2,]
> data.3
time value type
1 1 1 value1
3 3 2 value1
4 1 2 value2
5 2 0 value2
6 3 2 value2
data.remove_type1 <- data.2[data.2$type != "value1",]
> data.remove_row
time value type
4 1 2 value2
5 2 0 value2
6 3 2 value2
data.rename_row <- data.remove_type1
rownames(data.rename_row) <- seq(nrow(data.rename_row))
> data.rename_row
time value type
1 1 2 value2
2 2 0 value2
3 3 2 value2
gather()
进行data.frame数据格式转换前面用到的data、data.2具有不同的形式,前者可能更易读,但是大多数数据处理包和绘图包使用第二种形式。这时可以用tidyr
包中的gather()
进行数据格式的转换。
> data
time value1 value2
1 1 1 2
2 2 2 0
3 3 2 2
> data.2
time value type
1 1 1 value1
2 2 2 value1
3 3 2 value1
4 1 2 value2
5 2 0 value2
6 3 2 value2
函数参数如下,
gather(data, key = "key", value = "value", ..., na.rm = FALSE, convert = FALSE, factor_key = FALSE)
从data到data.2如下操作即可
library(tidyr)
gather(data, value, type, -time)
# time type value
# 1 1 value1 1
# 2 2 value1 2
# 3 3 value1 2
# 4 1 value2 2
# 5 2 value2 0
# 6 3 value2 2
简要说下可能的实现方式。
gather()
函数会将原数据转置区的列名一行视为主键列key,其余行、列的数据依次对应主键变成value列,我们只需要给新的key、value列命名即可。给个自己的数据当例子。
depth | sensor | 4 | 33 | 62 | 91 | 120 |
---|---|---|---|---|---|---|
1 | 1 | 0.9784983 | 0.9559506 | 0.9763369 | 0.9659561 | 0.9796494 |
2 | 1 | 0.9897878 | 0.9811474 | 0.9964173 | 0.9894787 | 0.9758862 |
1 | 2 | 0.9286928 | 0.9318475 | 0.9516243 | 0.9361377 | 0.9450460 |
2 | 2 | 0.9376116 | 0.9534188 | 0.9578816 | 0.9456095 | 0.9516801 |
1 | 3 | 0.9525189 | 0.9617189 | 0.9625706 | 0.9733991 | 0.9672283 |
2 | 3 | 0.9681960 | 0.9869330 | 0.9858327 | 0.9717086 | 0.9831383 |
每个深度有3个传感器,一共有两个深度,测量间隔30 s共测量了120 s。
选择非转置列,重命名新key、value列即可。
library(tidyr)
data.example <- read.csv("text.csv")
gather(data.example, time, value, -depth, -sensor)
# depth sensor time value
# 1 1 1 4 0.9784983
# 2 2 1 4 0.9897878
# 3 1 2 4 0.9286928
# 4 2 2 4 0.9376116
# 5 1 3 4 0.9525189
# 6 2 3 4 0.9681960
读取数据时如果原文件列名、行名为数字,读取后data.frame可能会在数字前加上前缀"X"、“V”。
data.file <- read.csv("data_file.csv")
# v1 v2 V3
# 1 1 2 3
# 2 1 2 3
# 3 1 2 3
colnames(data.file) <- c(1:length(colnames(data.file))
data.file2 <- read.csv("data_file2.csv")
# time X0 X10 X15 X17 X18
# 1 value 1 2 3 4 5
colnames(data.file2) <- c(colnames(data.file2)[1],
sapply(sapply(colnames(data.file2)[-1],substr,2,10), as.numeric))
# time 0 10 15 17 18
# 1 value 1 2 3 4 5
内部sapply
对指定范围取子字符串,从第2位到第10位(最大位即可,想写函数复用使用max(nchar(colnames(a)))
得到最长字符数),外部sapply
转为数值类型。
P.S.
使用Python pandas时经常弄混两个的函数,考虑写在一起比较还是用一样的模板分开写,欢迎建议。
有问题指出、补充欢迎评论区交流。