4 DataFrames.jl

    此处的省略号表示这是一张非常长的表,但只显示了少数行。 在分析数据时,我们经常会提出一些关于数据的有趣问题,这也称为 数据查询。 对于大型表格,计算机能够比手工查询更快地回答此类问题。 一些 数据查询 问题的例子如下:

    • 哪个电视节目评分最高?
    • 哪些电视节目由美国制作?
    • 哪些电视节目由相同的国家制作?

    但是,作为研究人员,实际的科学往往从多张表格或多个数据源开始。 例如,如果我们也有其他人的电视节目评分数据 (表 2):

    Table 2: Ratings.
    namerating
    Game of Thrones7
    Friends6.4

    现在则能够提出以下问题:

    • 节目 Game of Thrones 的平均评分是多少?
    • 谁对 Friends 给出了最高的评分?
    • 哪些节目你评分了,但其他人没有?

    在本章的其余部分中,我们将展示如何借助 Julia 来轻松地回答这些问题。 因此此,首先说明为什么需要 Julia 包 。 下节将展示如何使用此包,最后将展示如何编写快速数据变换的代码 (Section )。

    首先查看如下的成绩表 表 3

    Table 3: Grades for 2020.
    nameagegrade_2020
    Bob175.0
    Sally181.0
    Alice208.5
    Hank194.0

    其中 name 列的类型为 string, age 列的类型为 integer,而 grade 列的类型为 float

    截至目前,本书只介绍了 Julia 的基础知识。 这些基础能够处理很多东西,但不能处理表。 因此,为了说明我们需要更多类型,让我们尝试将表格数据存储在数组中:

    1. function second_row()
    2. name, age, grade_2020 = grades_array()
    3. i = 2
    4. row = (name[i], age[i], grade_2020[i])
    5. end
    6. second_row()

      或者,如果想获得 Alice 的成绩,首先需要弄清楚 Alice 所在的行:

      1. function row_alice()
      2. names = grades_array().name
      3. i = findfirst(names .== "Alice")
      4. end
      5. row_alice()
      1. 3

      然后才能得到成绩:

      1. 8.5

      DataFrames.jl 可以很容易地处理此类问题。 首先使用 using 加载 DataFrames.jl

      通过 DataFrames.jl,我们可以定义 DataFrame 来存储表格数据:

      1. names = ["Sally", "Bob", "Alice", "Hank"]
      2. grades = [1, 5, 8.5, 4]
      3. df = DataFrame(; name=names, grade_2020=grades)

      即此处返回的变量 df 以表格格式存储数据。

      1. function grades_2020()
      2. grade_2020 = [1, 5, 8.5, 4]
      3. DataFrame(; name, grade_2020)
      4. end
      5. grades_2020()
      Table 4: Grades 2020.
      namegrade_2020
      Sally1.0
      Bob5.0
      Alice8.5
      Hank4.0

      注意, 和 grade_2020 会在函数返回后销毁,即它们仅在函数中可用。 这样做还有两个好处。 首先,读者可以清晰地看到 namegrade_2020 由谁所有:它们属于 2020 成绩表。 其次,很容易在书中的任何地方确定 grades_2020() 的输出。 例如,可以将数据赋给变量 df

      namegrade_2020
      Sally1.0
      Bob5.0
      Alice8.5
      Hank4.0

      改变 df 的内容:

      1. df = DataFrame(name = ["Malice"], grade_2020 = ["10"])

      而且仍然能够无损恢复数据:

      1. df = grades_2020()
      namegrade_2020
      Sally1.0
      Bob5.0
      Alice8.5
      Hank4.0

      当然,此处假设没有重新定义函数。 我们在本书中保证不会这样做,因为这是非常糟糕的做法。 我们不会 “改变” 函数,而是创建一个具有明确名称的新函数。

      因此,回到 DataFrames构造器。 如你所见,创建方法是将向量作为参数传递给 DataFrame 构造器。 你可以给定任何合法的 Julia 向量,并且 只要向量长度相同,就能成功构造 DataFrame。 重复的向量、Unicode 符号和任何类型的数字都可以:

      1. DataFrame = ["a", "a", "a"], δ = [π, π/2, π/3])
      σδ
      a3.141592653589793
      a1.5707963267948966
      a1.0471975511965976

      通常,您在代码中会创建函数来包装一个或多个作用于 DataFrame 的函数。 例如,可以创建函数来获取一个或多个 names 的成绩:

      1. function grades_2020(names::Vector{Int})
      2. df = grades_2020()
      3. df[names, :]
      4. end
      5. grades_2020([3, 4])

      使用函数来包装基本功能的这种方式,在编程语言和包中非常常见。 基本上,你可以把 Julia 和 DataFrames.jl 看作基本模块的提供者。 它们提供了相当 通用的 模块,从而你可以在此基础之上实现一些 特例 ,比如这个成绩例子。 借助这些基本模块,你可以编写数据分析脚本,控制机器人或任何你想要构造的东西。

      Jose Storopoli, Rik Huijzer, Lazaro Alonso, 刘贵欣 (中文翻译), 田俊 (中文审校)