8  Data import

You are reading the work-in-progress second edition of R for Data Science. This chapter is largely complete and just needs final proof reading. You can find the complete first edition at https://r4ds.had.co.nz.

8.1 Introduction

使用 R 包提供的数据是学习数据科学工具的好方法,但您希望在某个时候将所学到的知识应用到自己的数据中。 在本章中,您将学习将数据文件读入 R 的基础知识。

具体来说,本章将重点关注读取纯文本矩形文件。 我们将从处理列名称、类型和缺失数据等功能的实用建议开始。 然后,您将了解如何同时从多个文件读取数据以及将数据从 R 写入文件。 最后,您将学习如何在 R 中手工制作 data frames。

8.1.1 Prerequisites

在本章中,您将学习如何使用 readr 包在 R 中加载平面文件,该包是 tidyverse 核心的一部分。

library(tidyverse)
#> Warning: package 'tidyverse' was built under R version 4.2.3
#> Warning: package 'ggplot2' was built under R version 4.2.3
#> Warning: package 'tibble' was built under R version 4.2.3
#> Warning: package 'tidyr' was built under R version 4.2.3
#> Warning: package 'readr' was built under R version 4.2.3
#> Warning: package 'purrr' was built under R version 4.2.3
#> Warning: package 'dplyr' was built under R version 4.2.3
#> Warning: package 'stringr' was built under R version 4.2.2
#> Warning: package 'forcats' was built under R version 4.2.3
#> Warning: package 'lubridate' was built under R version 4.2.3

8.2 Reading data from a file

首先,我们将重点关注最常见的矩形数据文件类型:CSV,它是逗号分隔值(comma-separated values)的缩写。 简单的 CSV 文件如下所示。 第一行,通常称为标题行,给出列名称,接下来的六行提供数据。 各列由逗号分隔。

Student ID,Full Name,favourite.food,mealPlan,AGE
1,Sunil Huffmann,Strawberry yoghurt,Lunch only,4
2,Barclay Lynn,French fries,Lunch only,5
3,Jayendra Lyne,N/A,Breakfast and lunch,7
4,Leon Rossini,Anchovies,Lunch only,
5,Chidiegwu Dunkel,Pizza,Breakfast and lunch,five
6,Güvenç Attila,Ice cream,Lunch only,6

Table 8.1 显示了与表格相同的数据的表示形式。

Table 8.1: Data from the students.csv file as a table.
Student ID Full Name favourite.food mealPlan AGE
1 Sunil Huffmann Strawberry yoghurt Lunch only 4
2 Barclay Lynn French fries Lunch only 5
3 Jayendra Lyne N/A Breakfast and lunch 7
4 Leon Rossini Anchovies Lunch only NA
5 Chidiegwu Dunkel Pizza Breakfast and lunch five
6 Güvenç Attila Ice cream Lunch only 6

我们可以使用 read_csv() 将该文件读入 R。 第一个参数是最重要的:文件的路径。 您可以将路径视为文件的地址:该文件名为 students.csv,位于 data 文件夹中。

students <- read_csv("data/students.csv")
#> Rows: 6 Columns: 5
#> ── Column specification ─────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (4): Full Name, favourite.food, mealPlan, AGE
#> dbl (1): Student ID
#> 
#> ℹ 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.

如果项目的 data 文件夹中有 students.csv 文件,则上面的代码将起作用。 您可以从 https://pos.it/r4ds-students-csv 下载 students.csv 文件,也可以使用以下命令直接从该 URL 读取它:

students <- read_csv("https://pos.it/r4ds-students-csv")

当您运行 read_csv() 时,它会打印一条消息,告诉您数据的行数和列数、使用的分隔符(delimiter)以及列规范(按列包含的数据类型组织的列名称)。 它还打印出一些有关检索完整列规范以及如何消除此消息的信息。 该消息是 readr 不可或缺的一部分,我们将在 Section 8.3 中返回它。

8.2.1 Practical advice

读入数据后,第一步通常涉及以某种方式对其进行转换,以便更容易在其余分析中使用。 考虑到这一点,让我们再看一下 students 数据。

students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE  
#>          <dbl> <chr>            <chr>              <chr>               <chr>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2            2 Barclay Lynn     French fries       Lunch only          5    
#> 3            3 Jayendra Lyne    N/A                Breakfast and lunch 7    
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          6

favourite.food 列中,有一堆食物项目,然后是字符串 N/A,这应该是真正的 NA,R 会识别为 “not available”。 我们可以使用 na 参数来解决这个问题。 默认情况下,read_csv() 仅将该数据集中的空字符串("")识别为 NAs,我们希望它也识别字符串 "N/A"

students <- read_csv("data/students.csv", na = c("N/A", ""))

students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE  
#>          <dbl> <chr>            <chr>              <chr>               <chr>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2            2 Barclay Lynn     French fries       Lunch only          5    
#> 3            3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          6

您可能还会注意到 Student IDFull Name 列周围有反引号。 这是因为它们包含空格,违反了 R 变量名称的通常规则; 它们是非语法(non-syntactic)名称。 要引用这些变量,您需要用反引号将它们括起来,`

students |> 
  rename(
    student_id = `Student ID`,
    full_name = `Full Name`
  )
#> # A tibble: 6 × 5
#>   student_id full_name        favourite.food     mealPlan            AGE  
#>        <dbl> <chr>            <chr>              <chr>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

另一种方法是使用 janitor::clean_names() 来使用一些启发式方法将它们一次性全部变成 snake case1

students |> janitor::clean_names()
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan           age  
#>        <dbl> <chr>            <chr>              <chr>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

读入数据后的另一个常见任务是考虑变量类型。 例如,meal_plan 是一个具有一组已知可能值的分类变量,在 R 中应表示为一个因子(factor):

students |>
  janitor::clean_names() |>
  mutate(meal_plan = factor(meal_plan))
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan           age  
#>        <dbl> <chr>            <chr>              <fct>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

请注意,meal_plan 变量中的值保持不变,但变量名称下方表示的变量类型已从 character (<chr>) 更改为 factor ()。 您将在 Chapter 17 中了解有关 factors 的更多信息。

在分析这些数据之前,您可能需要修复 ageid 列。 目前,age 是一个 character 变量,因为其中一个观测结果被输入为 five,而不是数字 5。 我们将在 Chapter 21 中讨论解决此问题的详细信息。

students <- students |>
  janitor::clean_names() |>
  mutate(
    meal_plan = factor(meal_plan),
    age = parse_number(if_else(age == "five", "5", age))
  )

students
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <fct>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6

这里的一个新函数是 if_else(),它有三个参数。 第一个参数 test 应该是一个逻辑向量。 当 testTRUE 时,结果将包含第二个参数 yes 的值;当 testFALSE 时,结果将包含第三个参数 no 的值。 这里我们说如果 age 是字符串 "five",则将其设为 "5",如果不是则将其保留为 age。 您将在 Chapter 13 中了解有关 if_else() 和逻辑向量的更多信息。

8.2.2 Other arguments

我们还需要提及其他几个重要的参数,如果我们首先向您展示一个方便的技巧,它们将更容易演示:read_csv() 可以读取您创建的文本字符串并像 CSV 文件一样格式化:

read_csv(
  "a,b,c
  1,2,3
  4,5,6"
)
#> # A tibble: 2 × 3
#>       a     b     c
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

通常,read_csv() 使用数据的第一行作为列名,这是一个非常常见的约定。 但文件顶部包含几行 metadata 的情况并不少见。 您可以使用 skip = n 跳过前 n 行,或使用 comment = "#" 删除所有以(例如)# 开头的行:

read_csv(
  "The first line of metadata
  The second line of metadata
  x,y,z
  1,2,3",
  skip = 2
)
#> # A tibble: 1 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

read_csv(
  "# A comment I want to skip
  x,y,z
  1,2,3",
  comment = "#"
)
#> # A tibble: 1 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3

在其他情况下,数据可能没有列名称。 您可以使用 col_names = FALSE 告诉 read_csv() 不要将第一行视为标题,而是从 X1Xn 按顺序标记它们:

read_csv(
  "1,2,3
  4,5,6",
  col_names = FALSE
)
#> # A tibble: 2 × 3
#>      X1    X2    X3
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

或者,您可以向 col_names 传递一个字符向量,该向量将用作列名称:

read_csv(
  "1,2,3
  4,5,6",
  col_names = c("x", "y", "z")
)
#> # A tibble: 2 × 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1     1     2     3
#> 2     4     5     6

阅读实践中遇到的大多数 CSV 文件时,您只需要了解这些参数即可。 (对于其余的,您需要仔细检查 .csv 文件并阅读 read_csv() 的许多其他参数的文档。)

8.2.3 Other file types

一旦掌握了 read_csv(),使用 readr 的其他函数就很简单了;只需要知道要使用哪个函数即可:

  • read_csv2() 读取以分号分隔的(semicolon-separated)文件。 这些使用 ; 而不是 , 来分隔字段,并且在使用 , 作为小数点标记的国家/地区很常见。

  • read_tsv() 读取制表符分隔的(tab-delimited)文件。

  • read_delim() 读取带有任何分隔符的文件,如果您未指定分隔符,则会尝试自动猜测分隔符。

  • read_fwf() 读取固定宽度的文件。 您可以使用 fwf_widths() 按宽度指定字段,或使用 fwf_positions() 按字段位置指定字段。

  • read_table() 读取固定宽度文件的常见变体,其中列由空格分隔。

  • read_log() 读取 Apache 风格的日志文件。

8.2.4 Exercises

  1. 您将使用什么函数来读取字段以 “|” 分隔的文件?

  2. 除了 fileskipcomment 之外,read_csv()read_tsv() 还有哪些其他参数的共同点?

  3. read_fwf() 最重要的参数是什么?

  4. 有时 CSV 文件中的字符串包含逗号。 为了防止它们引起问题,它们需要被引号字符包围,例如 "'。默认情况下,read_csv() 假设引号字符为 "。 要将以下文本读入数据框中,您需要为 read_csv() 指定什么参数?

    "x,y\n1,'a,b'"
  5. 确定以下每个内联 CSV 文件的问题所在。 运行代码时会发生什么?

    read_csv("a,b\n1,2,3\n4,5,6")
    read_csv("a,b,c\n1,2\n1,2,3,4")
    read_csv("a,b\n\"1")
    read_csv("a,b\n1,2\na,b")
    read_csv("a;b\n1;3")
  6. 通过以下方式练习引用以下数据框中的非语法名称:

    1. 提取名为 1 的变量。
    2. 绘制 12 的散点图。
    3. 创建一个名为 3 的新列,即 2 除以 1
    4. 将列重命名为 onetwothree
    annoying <- tibble(
      `1` = 1:10,
      `2` = `1` * 2 + rnorm(length(`1`))
    )

8.3 Controlling column types

CSV 文件不包含有关每个变量类型的任何信息(i.e. whether it’s a logical, number, string, etc.),因此 readr 会尝试猜测类型。 本节介绍猜测过程的工作原理、如何解决导致其失败的一些常见问题,以及(如果需要)如何自行提供列类型。 最后,我们将提到一些通用策略,如果 readr 发生灾难性失败并且您需要更深入地了解文件结构,这些策略将非常有用。

8.3.1 Guessing types

readr 使用启发式方法来确定列类型。 对于每一列,它会提取从第一行到最后一行均匀间隔的 1,0002 行的值,忽略缺失值。 然后它会回答以下问题:

  • 它仅包含 FTFALSETRUE(忽略大小写)吗?如果是这样,那就合乎逻辑了。
  • 它是否仅包含数字(e.g., 1, -4.5, 5e6, Inf)?如果是这样,它就是一个 number。
  • 是否符合 ISO8601 标准?如果是,则它是一个 date 或 date-time。(我们将在 ?sec-creating-datetimes 中更详细地返回 date-times)。
  • 否则,它必须是一个 string。

您可以在这个简单的示例中看到该行为的实际效果:

read_csv("
  logical,numeric,date,string
  TRUE,1,2021-01-15,abc
  false,4.5,2021-02-15,def
  T,Inf,2021-02-16,ghi
")
#> # A tibble: 3 × 4
#>   logical numeric date       string
#>   <lgl>     <dbl> <date>     <chr> 
#> 1 TRUE        1   2021-01-15 abc   
#> 2 FALSE       4.5 2021-02-15 def   
#> 3 TRUE      Inf   2021-02-16 ghi

如果你有一个干净的数据集,这种启发式方法会很有效,但在现实生活中,你会遇到一系列奇怪而美丽的失败。

8.3.2 Missing values, column types, and problems

列检测失败的最常见方式是列包含意外值,并且您得到的是字符列而不是更具体的类型。 造成这种情况的最常见原因之一是缺失值,该值是使用 readr 期望的 NA 之外的其他内容记录的。

以这个简单的 1 列 CSV 文件为例:

simple_csv <- "
  x
  10
  .
  20
  30"

如果我们在没有任何附加参数的情况下读取它,x 就会成为一个 character 列:

read_csv(simple_csv)
#> # A tibble: 4 × 1
#>   x    
#>   <chr>
#> 1 10   
#> 2 .    
#> 3 20   
#> 4 30

在这个非常小的情况下,您可以轻松地看到缺失值 .。 但是,如果您有数千行,其中只有少数由 .s 表示的缺失值,会发生什么情况? 一种方法是告诉 readr x 是一个 numeric 列,然后查看它在哪里失败。 您可以使用 col_types 参数来执行此操作,该参数采用命名列表,其中名称与 CSV 文件中的列名称匹配:

df <- read_csv(
  simple_csv, 
  col_types = list(x = col_double())
)
#> Warning: One or more parsing issues, call `problems()` on your data frame for
#> details, e.g.:
#>   dat <- vroom(...)
#>   problems(dat)

现在 read_csv() 报告存在问题,并告诉我们可以通过 problems() 找到更多信息:

problems(df)
#> # A tibble: 1 × 5
#>     row   col expected actual file                                           
#>   <int> <int> <chr>    <chr>  <chr>                                          
#> 1     3     1 a double .      C:/Users/13081/AppData/Local/Temp/RtmpmMITeQ/f…

这告诉我们,第 3 行第 1 列存在问题,其中 readr 期望为 double,但得到的是 .. 这表明该数据集使用 . 对于缺失值。 然后我们设置 na = ".",自动猜测成功,得到我们想要的 numeric 列:

read_csv(simple_csv, na = ".")
#> # A tibble: 4 × 1
#>       x
#>   <dbl>
#> 1    10
#> 2    NA
#> 3    20
#> 4    30

8.3.3 Column types

readr 总共提供了九种列类型供您使用:

  • col_logical()col_double() 读取逻辑数和实数。它们相对很少需要(除了上述情况),因为 readr 通常会为您猜测它们。
  • col_integer() 读取整数。在本书中,我们很少区分整数(integers)和双精度数(doubles),因为它们在功能上是等效的,但显式读取整数有时会很有用,因为它们占用双精度数一半的内存。
  • col_character() 读取字符串(strings)。当您有一列是数字标识符(即标识对象的一长串数字,但对其应用数学运算没有意义)时,这对于显式指定非常有用。示例包括电话号码、社会安全号码、信用卡号码等。
  • col_factor()col_date()col_datetime() 分别创建因子(factors)、日期(dates)和日期时间(date-times);当我们在 Chapter 17?sec-dates-and-times 中介绍这些数据类型时,您将了解更多信息。
  • col_number() 是一个宽松的数字解析器,它将忽略非数字组件,对于货币特别有用。您将在 Chapter 14 中了解更多信息。
  • col_skip() 会跳过一列,因此它不会包含在结果中,如果您有一个大型 CSV 文件并且只想使用某些列,这对于加快读取数据的速度很有用。

还可以通过从 list() 切换到 cols() 并指定 .default 来覆盖默认列:

another_csv <- "
x,y,z
1,2,3"

read_csv(
  another_csv, 
  col_types = cols(.default = col_character())
)
#> # A tibble: 1 × 3
#>   x     y     z    
#>   <chr> <chr> <chr>
#> 1 1     2     3

另一个有用的帮助器是 cols_only() ,它只会读取您指定的列:

read_csv(
  another_csv,
  col_types = cols_only(x = col_character())
)
#> # A tibble: 1 × 1
#>   x    
#>   <chr>
#> 1 1

8.4 Reading data from multiple files

有时,您的数据会分割在多个文件中,而不是包含在单个文件中。 例如,您可能有多个月的销售数据,每个月的数据都位于单独的文件中:01-sales.csv for January, 02-sales.csv for February, and 03-sales.csv for March。 使用 read_csv(),您可以立即读取这些数据并将它们堆叠在单个数据框中。

sales_files <- c("data/01-sales.csv", "data/02-sales.csv", "data/03-sales.csv")
read_csv(sales_files, id = "file")
#> # A tibble: 19 × 6
#>   file              month    year brand  item     n
#>   <chr>             <chr>   <dbl> <dbl> <dbl> <dbl>
#> 1 data/01-sales.csv January  2019     1  1234     3
#> 2 data/01-sales.csv January  2019     1  8721     9
#> 3 data/01-sales.csv January  2019     1  1822     2
#> 4 data/01-sales.csv January  2019     2  3333     1
#> 5 data/01-sales.csv January  2019     2  2156     9
#> 6 data/01-sales.csv January  2019     2  3987     6
#> # ℹ 13 more rows

再次强调,如果项目的 data 文件夹中有 CSV 文件,则上面的代码将起作用。 您可以从 https://pos.it/r4ds-01-sales, https://pos.it/r4ds-02-sales, and https://pos.it/r4ds-03-sales 下载这些文件,或者您可以直接读取它们:

sales_files <- c(
  "https://pos.it/r4ds-01-sales",
  "https://pos.it/r4ds-02-sales",
  "https://pos.it/r4ds-03-sales"
)
read_csv(sales_files, id = "file")

id 参数将一个名为 file 的新列添加到结果数据框中,用于标识数据来自的文件。 当您正在阅读的文件没有可帮助您将观察结果追溯到其原始来源的识别列的情况下,这尤其有用。

如果您想要读入许多文件,则将它们的名称写为列表可能会很麻烦。 相反,您可以使用基本的 list.files() 函数通过匹配文件名中的模式来查找文件。 您将在 Chapter 16 中了解有关这些模式的更多信息。

sales_files <- list.files("data", pattern = "sales\\.csv$", full.names = TRUE)
sales_files
#> [1] "data/01-sales.csv" "data/02-sales.csv" "data/03-sales.csv"

8.5 Writing to a file

readr 还附带两个有用的函数,用于将数据写回磁盘:write_csv()write_tsv()。 这些函数最重要的参数是 x(要保存的数据框)和 file(保存数据的位置)。 您还可以指定如何使用 na 写入缺失值,以及是否要 append 到现有文件。

write_csv(students, "students.csv")

现在让我们重新读取该 csv 文件。 请注意,保存到 CSV 时,您刚刚设置的变量类型信息会丢失,因为您要再次从纯文本文件中读取:

students
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <fct>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6
write_csv(students, "students-2.csv")
read_csv("students-2.csv")
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan             age
#>        <dbl> <chr>            <chr>              <chr>               <dbl>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
#> 2          2 Barclay Lynn     French fries       Lunch only              5
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
#> 4          4 Leon Rossini     Anchovies          Lunch only             NA
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
#> 6          6 Güvenç Attila    Ice cream          Lunch only              6

这使得 CSVs 对于缓存临时结果有点不可靠 — 每次加载时都需要重新创建列规范。 有两个主要替代方案:

  1. write_rds()read_rds() 是基本函数 readRDS()saveRDS() 的统一包装器。 它们以 R 的自定义二进制格式(称为 RDS)存储数据。 这意味着当您重新加载对象时,您将加载与存储的完全相同的 R 对象。

    write_rds(students, "students.rds")
    read_rds("students.rds")
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <fct>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6
  2. arrow 包允许您读取和写入 parquet 文件,这是一种可以跨编程语言共享的快速二进制文件格式。 我们将在 ?sec-arrow 中更深入地回到 arrow。

    library(arrow)
    write_parquet(students, "students.parquet")
    read_parquet("students.parquet")
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <fct>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    NA                 Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6

Parquet 往往比 RDS 快得多,并且可以在 R 之外使用,但需要 arrow 包。

8.6 Data entry

有时,您需要在 R 脚本中输入一些数据,“手动”组装 tibble。 有两个有用的函数可以帮助您执行此操作,这两个函数的不同之处在于您是按列还是按行布局 tibble。 tibble() 按列工作:

tibble(
  x = c(1, 2, 5), 
  y = c("h", "m", "g"),
  z = c(0.08, 0.83, 0.60)
)
#> # A tibble: 3 × 3
#>       x y         z
#>   <dbl> <chr> <dbl>
#> 1     1 h      0.08
#> 2     2 m      0.83
#> 3     5 g      0.6

按列布局数据可能会让人很难看出行之间的关系,因此另一种选择是 tribble(),它是 transposed tibble 的缩写,它可以让您逐行布局数据。 tribble() 是为代码中的数据输入而定制的:列标题以 ~ 开头,条目之间用逗号分隔。 这使得以易于阅读的形式布置少量数据成为可能:

tribble(
  ~x, ~y, ~z,
  1, "h", 0.08,
  2, "m", 0.83,
  5, "g", 0.60
)
#> # A tibble: 3 × 3
#>       x y         z
#>   <dbl> <chr> <dbl>
#> 1     1 h      0.08
#> 2     2 m      0.83
#> 3     5 g      0.6

8.7 Summary

在本章中,您学习了如何使用 read_csv() 加载 CSV 文件以及如何使用 tibble()tribble() 进行自己的数据输入。 您已经了解了 csv 文件的工作原理、可能遇到的一些问题以及如何克服这些问题。 我们将在本书中多次介绍数据导入:Chapter 21 来自 Excel 和 Google Sheets,?sec-import-databases 将向您展示如何从数据库加载数据,?sec-arrow 来自 parquet 文件,Chapter 24 来自 JSON ,Chapter 25 来自网站。

我们即将读到本书这一部分的结尾,但最后还有一个重要的主题要讨论:如何获得帮助。 因此,在下一章中,您将了解一些寻求帮助的好地方、如何创建 reprex 以最大限度地提高获得良好帮助的机会,以及一些与 R 世界保持同步的一般建议。


  1. The janitor package is not part of the tidyverse, but it offers handy functions for data cleaning and works well within data pipelines that use |>.↩︎

  2. You can override the default of 1000 with the guess_max argument.↩︎