北京大学生物信息平台论坛

 找回密码
 立即注册
搜索
热搜: 通知 活动

R 学习笔记:R 数据类型

[复制链接]
wolf 发表于 2016-4-20 16:47:57 | 显示全部楼层 |阅读模式
本帖最后由 wolf 于 2016-5-11 19:36 编辑

R 数据类型
R 的数据存储类型基本类型

最基本的类型是存储单一数值的类型. 主要包括 Numeric, Integer, Complex, Character, Logical 等.

数字

Numeric 或者 “double” 是 R 优先选择的存储数值的方式, 相当于 C 中的 “double”. 需要注意的是, 有的时候认为 Numeric 是 “integer” 和 “double” 的统称. .Machine$double.double.eps 等变量给出环境中的存储 double 的限度.

Integer 是整数, 相当于 C 中的 “int”. 一般不管有没有小数点的数字, R 默认存成 Numeric, 这个时候需要使用 as.integer 函数强制去把数存为 Integer..Machine$integer.max 给出能够存储的最大的整数, 总是 2^31 - 1 = 2147483647 .

Complex 是复数的存储形式.

字符

Character 是存储字符和字符串的类型. 字符串和字符都可以存储.

逻辑

Logical 是存储 bool 值的类型, 只有 TRUE (T) 和 FALSE (F) 两个值.

时间

Date 类型是专门为存储时间设计的. POSIXct 把时间保存为一个整数, 为某时间距离 1970 年 1 月 1 日的时间. POSIXlt 则保存一个列表, 其中有年月日时分秒的信息. 使用 unclass 可以把相应的类转换为基本类. 相应函数有 as.POSIXct, as. POSIXlt, strptime, strftime, ISOdate, ISOdatetime 等, 还有 chron 包可以处理时间.


数据结构

数据往往不是单一的值, R 有很好的存储多值数据的结构.


向量

vector, 向量, 类似一维的 array, 存储相同的基本类型, 如果有字符型的元素, 则所有的值都会转为字符型. 需要注意的是, 长度为 1 的也可以是 vector, 所以使用 is.vector 函数进行判断也会显示 TRUE, 也就是说所有单一的基本元素变量都会被认为是一个 vector. 而对于不同长度的 vector, 使用 class 或者 mode 等函数去判断其类型的话, 都会给出其基本元素的类型, 如果一个 vector 中所有元素都是 character 则返回 character.

这样的方式给人的策略是 R 语言试图忽视单复数的差别. 其实把单一数据和向量的类型不做太多区分正是 R 语言的特点的反应. R 语言用于处理大量数据, 那么其构造和逻辑就更符合这样的需求. 例如, 如果对一个实际上长度不为 1 的整数向量加 1, 得到的是和原向量的每个值加 1 的新的向量.


  1. x <- 1:6
  2. x + 1
  3. [1] 2 3 4 5 6 7
复制代码


如果是两个等长向量相加, 则是对应的元素一个个相加.


  1. x <- 1:6
  2. y <- 1:6
  3. x + y
  4. [1]  2  4  6  8 10 12
复制代码



如果两个向量长度不等长, 那么只有一个长度是另一个长度的倍数才能相加. 而短的向量则会重复为长度和长向量一样长. 其实这样看也就理解了当短向量长度为 1 的时候的结果其实是个特例. 这也是 R 语言淡化单一值和向量之间区别的一个特点.

  1. x <- 1:6
  2. z <- 1:2
  3. x + z
  4. [1] 2 4 4 6 6 8
复制代码



因子

Factor 是一种能够帮助节省内存空间的方式. 如果一系列的值中有较多重复的值, 可以使用 factor, factor 中只会存储一份原值, 而原来的值本身会存为数字, 这样就会节省空间.

原值被称为水平 (level), 可以自己去设置顺序等. levels 函数可以返回一个 factor 所有可能的 level, 而 nlevels 则可以返回 level 的个数.

把一个向量转换成 factor 只要使用 as.factor 函数. 有的时候需要我们把数字先转换成 factor, 经过一定处理之后需要再把 factor 转换称为数字, 这个时候不能直接使用 as.numeric, 因为 as.numeric 会直接返回 factor 内部的值, 而不是原来的值. 我们需要先使用 as.character 或者 levels 先得到字符, 然后再转换为数字.


  1. myfactor <- factor(c(10, 20, 20, 50, 20, 10), levels=c(10,20,50), ordered=TRUE)
  2. as.numeric(levels(myfactor)[myfactor])
  3. as.numeric(as.character(myfactor))
复制代码


有的时候我们会需要去生成某种类型的 factor 来做参数或者测试数据, 那么可以使用 gl 函数. gl 函数可以记成 “generate levels” 的缩写. gl 函数的参数主要有: n 用来设置 level 的个数; k 用来设置每个 level 重复的次数; length 来设置长度, 其实有了前两个参数这个可以忽略; labels 用来设置 level 的值; ordered 接受 bool 值, 设置是否 level 是排列好顺序的.


需要注意的是, 在使用 c 函数把若干个因子组合在一起的时候, 需要先把因子转换为原来的值再使用 c 函数, 否则 c 函数直接把因子当作存在内存中的数字而丢失原来的意思.

如果我有一个向量, 其中是连续的值, 我现在想要画直方图, 我可以直接用相应的函数画出来. 如果我不需要看到图, 而只想要知道这些值都怎么分布在区间内, 我就可以使用 cut 函数. cut 函数把数值分成不同的区间, 然后把原来的向量转化为 level 为区间的因子, 这样就能知道某个值属于哪个区间. 原向量和因子的长度相等, 因子的 level 是自己设定的. 使用 table 函数就可以统计每个区间中数值的个数.


  1. aaa <- c(1,2,3,4,5,2,3,4,5,6,7)

  2. cut(aaa, 3)
  3. [1] (0.994,3] (0.994,3] (0.994,3] (3,5]     (3,5]     (0.994,3] (0.994,3]
  4. [8] (3,5]     (3,5]     (5,7.01]  (5,7.01]
  5. Levels: (0.994,3] (3,5] (5,7.01]

  6. cut(aaa, 3, dig.lab = 4, ordered = TRUE)
  7. [1] (0.994,3] (0.994,3] (0.994,3] (3,5]     (3,5]     (0.994,3] (0.994,3]
  8. [8] (3,5]     (3,5]     (5,7.006] (5,7.006]
  9. Levels: (0.994,3] < (3,5] < (5,7.006]
复制代码


有的时候, 我需要去了解两个因子之间有多少组合, 这时候就可以选择 interaction 函数, interaction 函数可以给出多个因子 level 的组合. 这些组合并不都有数据, 如果设置 drop = TRUE 则会扔掉没有数据的 level, 而只保留真的有数据的 level.

  1. a <- gl(2, 4, 8)
  2. b <- gl(2, 2, 8, labels = c("ctrl", "treat"))
  3. interaction(a, b, drop = TRUE, sep = ".")
  4. #[1] 1.ctrl  1.ctrl  1.treat 1.treat 2.ctrl  2.ctrl  2.treat 2.treat
  5. #Levels: 1.ctrl 2.ctrl 1.treat 2.treat
复制代码



矩阵

matrix, 二维的 array, 所有的元素都是相同类型. as.matrix, is.matrix.

在取 matrix 中的元素的时候, 可以使用下标来操作. 一般情况下, 下标按照 [ROW, COL] 来取, 其中 ROW 和 COL 都可以是向量, 可以是指示想要取出的行号或者列号的向量, 也可以是 bool 值的向量. 如果方括号中没有逗号, 而按照 [NUM] 来取矩阵中的元素, 则会返回把矩阵当作一维的向量排列后对应位置的值, 如果是 2 乘 2 矩阵, [3] 则会返回 [2,1] 的值, 原矩阵会按照列优先延展成向量.

在取矩阵的一个行或者列后, 其返回值的维度会减少, 在取下标的时候, 使用参数 [,, drop = FALSE] 则不会让矩阵所取结果的值的维度减少.

矩阵在内存中是按照行优先或者列优先存储的一维的向量, 所以如果需要建立一个矩阵, 最好是先建立一个足够大的矩阵, 然后向其中填数, 而不应该去建立一个小的矩阵, 然后使用 rbind 或着 cbind 去补充. 因为如果矩阵的数量增加, R 需要重新申请空间, 并且, 如果添加的行或者列和存储的优先不一样的话, 则需要重新对矩阵元素进行排序, 这样的话就会使得效率十分低下. 所以, 建立矩阵应该建立足够大的矩阵, 如果最后不需要这么大的矩阵, 只要重新赋值一次就好.


数组

array, 可以有很多维.


列表

list, 列表, 可以把不同类型的变量组合在一起, list 中也可以包含子 list.

取列表中的元素就需要特别注意, 如果使用单方括号 “[]”, 所取出的结果就是该列表的一个子列表, 而如果想要获得其本身内容, 则需要使用双方括号 “[[]]” 或者美元符号 “$”. 取元素可以使用名称, 也可以使用数字.

  1. mylist <- list(one = "one", two = c(2, 2))
  2. mylist
  3. #$one
  4. #[1] "one"

  5. #$two
  6. #[1] 2 2

  7. mylist["one"]
  8. #$one
  9. #[1] "one"

  10. mylist[["one"]]
  11. #[1] "one"
  12. mylist$one
  13. #[1] "one"
复制代码


由于列表中可以放各种类型的对象, 这就为把多种多样相关的数据整合在一起提供了便利. 一个 list 可以不准确地看作是 C++ 语言中专门用来存储数据的 class. 一些相关变量, 为了和其他的变量区分, 往往会取相似的变量名, 当变量十分多的时候, 这样的方法依然不方便查询. 我们就可以把这些相关的变量都放在一个列表中, 然后通过取下标的方法访问变量. 如果忘记了变量名, 还可以通过 names 函数或者 str 函数去查询包含在列表中的变量名. 这样的方法十分适合保存对多个数据集进行相同或相似的处理结果, 可以使用 for 循环来完成数据的保存.



  1. rna.gene.fpkm  <- list()  # 需要提前建立空的列表.
  2. for (nam in dir(rna.cuffnorm.result.dir)) {
  3.     rna.gene.fpkm[[nam]]  <- read.table(  # 建立列表元素并赋值
  4.         file.path("./",
  5.                   nam,
  6.                   "genes.fpkm_table"),
  7.         header = TRUE,
  8.         sep    = "\t")
  9. }
复制代码
如果想要删除列表中的一个元素, 则只要向该元素赋值 NULL 即可. 如果想要设置一个值为 NULL 的元素, 则应该向该元素赋值一个 `list(NULL)`.



  1. x <- list(a = 1, b = 2)
  2. x[["b"]] <- NULL
  3. str(x)
  4. #> List of 1
  5. #> $ a: num 1

  6. y <- list(a = 1)
  7. y["b"] <- list(NULL)
  8. str(y)
  9. #> List of 2
  10. #> $ a: num 1
  11. #> $ b: NULL
复制代码





列表在内存中的保存并不是连续的, 而是像 C 语言中的链表一样的分散开的, 所以对列表增加元素并不像矩阵那样效率低, 对于列表或者相应的数据框, 使用 rbind 则不会像对矩阵使用那么缓慢. 这也是为什么, 我们可以先建立一个空列表 (或数据框), 然后通过 for loop 等方式逐渐向其中高效地添加元素.


数据框

data.frame, 数据框, 是一种特别的 list, 其像 matrix 一样限制了每列的变量长度必须要一样, 但同时也像 list 一样, 每一列的变量类型可以不同. 数据框实际上看着非常像我们常用的 Excel 中的一个表, 数据框的列名和行名也分别对应于 Excel 表格中的列名和行名. 由于数据框具有 list 和 matrix 的特点, 所以对数据框进行取元素也有 list 和 matrix 取元素的特点, 我们既可以像 list 一样使用 “$” 取一个列, 也可以像 matrix 一样使用 “[ROW, COL]” 取其中一个特定的值.


查询变量的类型

常用的查询变量类型的函数有: mode, storage.mode, class 和 typeof. 这些函数有一些差别.

  • storage.mode 是数据实际在内存中存储所采用的方式.
  • class 是面向对象的 R, 比如说 data.frame 实际上的存储方式 (storage.mode) 是 list, 但是为了更好处理表单数据, 就包装成为了 data.frame 类型.
  • mode 和 typeof 给出的结果很接近, 是实际上的类型, 但是在 mode 中, “integer” 和 “double” 都被认为是 “numeric”.

如果需要知道环境中每个基本类型能存储的大小, 可以查询 .Machine 这个 list, 相应的大小存储在该 list 中.


NA

由于种种原因, 数据中可能会出现缺失值的情况, R 会用 NA 来替代相应的空值. 如果是计算后产生的空值, 则会用 Inf 或者 NaN 来代替. 处理空值是数据分析中的必要部分.

  • 判断 NA 可以使用 is.na 或者 is.nan 函数, 其中 is.na 会把 NA, Inf, NaN 都认为是 NA, 而 is.nan 则只关注 NaN.
  • mean, var, sum, min, max, 等函数都有 na.rm 参数, 设置成真, 则会在计算的时候把 NA 给除去.
  • lm, glm, gam 等函数有 na.action 参数, 该参数接受函数作为变量, 如 na.omit, na.fail. na.pass, na.exculde 等.
  • na.omit 和 complete.cases 都可以返回一个只包含完整数据行的 data.frame, 也就是说如果一行中有一个或多个 NA, 该行就会被剔除.
  • 对于 read.table 等函数可以使用 na.strings 可以把特定的数值或字符认为是 NA.



回复

使用道具 举报

北京大学生物信息平台论坛

GMT+8, 2017-11-19 22:10 , Processed in 0.080633 second(s), 23 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表