这可以通过 usingimport实现。 本书将使用 using 导入代码:

    在执行上述操作后,就可以使用 ModuleName 中所有的函数和类型。

    了解如何处理日期和时间戳在数据科学中很重要。 正如在 为什么选择 Julia? (Section ) 节讨论的那样,Python 中的 pandas 使用它自己的 datetime 类型处理日期。 R 语言中 TidyVerse 的 lubridate 包中也是如此,它也定义了自己的 datetime 类型来处理日期。 在 Julia 软件包中,不需要编写自己的日期逻辑,因为 Julia 标准库中有一个名为 Dates 的日期处理模块。

    首先加载 Dates 模块到工作空间中:

    1. using Dates

    3.5.1.1 Date and DateTime Types

    Dates 标准库模块有 两种处理日期的类型:

    1. Date: 表示以天为单位的时间和
    2. DateTime: 表示以毫秒为单位的时间。

    构造 DateDateTime 的方法是,向默认构造器传递表示年,月,日,小时等等的整数:

    1. Date(1987) # year
    1. 1987-01-01
    1. Date(1987, 9) # year, month
    1. 1987-09-01
    1. Date(1987, 9, 13) # year, month, day
    1. 1987-09-13
    1. DateTime(1987, 9, 13, 21) # year, month, day, hour
    1. 1987-09-13T21:00:00
    1. DateTime(1987, 9, 13, 21, 21) # year, month, day, hour, minute
    1. 1987-09-13T21:21:00

    好奇的人会发现,1987 年 9 月 13 日 21 点 21 分正是第一作者 Jose 的官方出生时间。

    也可以向默认构造器传递 Period 类型。 对于计算机来说,Period 类型是时间的等价表示。 Julia 的 Dates 具有如下的 Period 抽象类型:

    1. subtypes(Period)
    1. DatePeriod
    1. TimePeriod

    它被划分为如下的具体类型,并且它们的用法都是不言自明的:

    1. subtypes(DatePeriod)
    1. Day
    1. Month
    1. Quarter
    1. Week
    1. Year
    1. subtypes(TimePeriod)
    1. Hour
    1. Microsecond
    1. Millisecond
    1. Minute
    1. Nanosecond
    1. Second

    因此,也能以如下方式构造 Jose 的官方出生时间:

    1. DateTime(Year(1987), Month(9), Day(13), Hour(21), Minute(21))
    1. 1987-09-13T21:21:00

    3.5.1.2 序列化 Dates

    多数情况下,我们不会从零开始构造 DateDateTime 示例。 实际上更可能是 将字符串序列化为 DateDateTime 类型

    DateDateTime 构造器可以接收一个数字字符串和格式字符串。 例如,表示 1987 年 9 月 13 日的字符串 "19870913" 可被序列化为:

    1. Date("19870913", "yyyymmdd")
    1. 1987-09-13

    注意第二个参数是日期格式的字符串表示。 前四位表示年 y,后接着的两位表示月 m, 而最后两位数字表示日 d.

    这也适用于 DateTime 的时间戳:

    1. DateTime("1987-09-13T21:21:00", "yyyy-mm-ddTHH:MM:SS")
    1. 1987-09-13T21:21:00

    可以在 Julia Dates’ documentation 了解到更多的日期格式。 不同担心需要时常浏览文档,我们在处理日期和时间戳时也是这样。

    根据 ,当只需调用几次时,使用 Date(date_string, format_string) 方法也是可以的。 然而,如果需要处理大量相同格式的日期字符串,那么更高效的方法是先创建 DateFormat 类型,然后传递该类型而不是原始的格式字符串。 然后,先前的例子改为:

    1. format = DateFormat("yyyymmdd")
    2. Date("19870913", format)
    1. 1987-09-13

    或者,在不损失性能的情况下,使用字符串字面量前缀 dateformat"..."

    1. Date("19870913", dateformat"yyyymmdd")
    1. 1987-09-13

    3.5.1.3 提取日期信息

    很容易 DateDateTime 对象中提取想要的信息。 首先,创建一个具体日期的实例:

    1. my_birthday = Date("1987-09-13")
    1. 1987-09-13

    然后可以从 my_birthday 中提取任何想要的信息:

    1. year(my_birthday)
    1. 1987
    1. month(my_birthday)
    1. 9
    1. 13
      1. (1987, 9)
      1. monthday(my_birthday)
      1. (9, 13)
      1. (1987, 9, 13)

      也能了解该日期是一周的第几天和其他方便的应用:

      1. dayofweek(my_birthday)
      1. 7
      1. dayname(my_birthday)
      1. Sunday
      1. dayofweekofmonth(my_birthday)
      1. 2

      是的,Jose 出生在 9 月的第 2 个星期日。

      3.5.1.4 日期操作

      可以对 Dates 实例进行多种 操作 。 例如,可以对一个 DateDateTime 实例增加天数。 请注意,Julia 的 Dates 将自动地对闰年以及 30 天或 31 天的月份执行必要的调整(这称为 日历 算术)。

      1. my_birthday + Day(90)
      1. 1987-12-12

      我们想加多少天就加多少天:

      1. my_birthday + Day(90) + Month(2) + Year(1)
      1. 1989-02-11

      可能你想知道:“还能用 Dates 做些什么?还有哪些可用的方法?”,则可以使用 methodswith 检索这些方法。 这里只展示前 20 条结果:

      1. first(methodswith(Date), 20)
      1. [1] show(io::IO, dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/io.jl:736
      2. [2] show(io::IO, ::MIME{Symbol("text/plain")}, dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/io.jl:734
      3. [3] DateTime(dt::Date, t::Time) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/types.jl:403
      4. [4] Day(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
      5. [5] Month(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
      6. [6] Quarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
      7. [7] Week(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
      8. [8] Year(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
      9. [9] firstdayofmonth(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:84
      10. [10] firstdayofquarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:157
      11. [11] firstdayofweek(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:52
      12. [12] firstdayofyear(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:119
      13. [13] lastdayofmonth(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:100
      14. [14] lastdayofquarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:180
      15. [15] lastdayofweek(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:68
      16. [16] lastdayofyear(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:135
      17. [17] +(dt::Date, t::Time) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:19
      18. [18] +(dt::Date, y::Year) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:27
      19. [19] +(dt::Date, z::Month) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:54
      20. [20] +(x::Date, y::Quarter) in Dates at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:73

      由上可知,也能使用加 + 和减 - 运算符。 让我们看看 Jose 的年龄, 以天为单位:

      1. today() - my_birthday
      1. 12836 days

      Date 类型的 默认持续时间Day 实例。 而对于 DateTime 类型,默认持续时间是 Millisecond 实例。

      1. DateTime(today()) - DateTime(my_birthday)
      1. 1109030400000 milliseconds

      3.5.1.5 日期区间

      关于 Dates 模块的一个好处是,可以轻松地构造 日期和时间区间。 Julia 足够聪明,因此不用去定义在 Section 中讨论的整个区间类型和操作。 它只需将为 range 定义的函数和操作扩展到 Date 类型。 这就是在 为什么选择 Julia? (Section 2) 中讨论过的多重派发。

      例如,假设想要创建一个 Day 区间。 这可以轻松地通过冒号 : 运算符实现:

      1. Date("2021-01-01"):Day(1):Date("2021-01-07")
      1. 2021-01-01
      1. 2021-01-02
      1. 2021-01-03
      1. 2021-01-04
      1. 2021-01-05
      1. 2021-01-06
      1. 2021-01-07

      使用 Day(1) 作为间隔没有什么特别的, 可以使用 任意的 Period 类型 作为间隔。 比如,使用 3 天作为间隔:

      1. Date("2021-01-01"):Day(3):Date("2021-01-07")
      1. 2021-01-01
      1. 2021-01-04
      1. 2021-01-07

      又或者是月份:

      1. Date("2021-01-01"):Month(1):Date("2021-03-01")
      1. 2021-01-01
      1. 2021-02-01
      1. 2021-03-01

      注意, 这个区间的类型是内含 Date 和具体 Period 类型的StepRange,其中 Period 用于作为间隔:

      1. date_interval = Date("2021-01-01"):Month(1):Date("2021-03-01")
      2. typeof(date_interval)

        可以使用 collect 函数将它转换为 向量

        1. collected_date_interval = collect(date_interval)
        1. 2021-01-01
        1. 2021-03-01

        并具有全部的可用数组功能,例如索引:

        1. collected_date_interval[end]
        1. 2021-03-01

        也可以在 Date 向量中实现 日期操作的广播

        1. collected_date_interval .+ Day(10)
        1. 2021-01-11
        1. 2021-02-11
        1. 2021-03-11

        同理,这些例子也适用于 类型。

        Random 模块是另一重要的 Julia 标准库模块。 这个模块的用途是 生成随机数Random 是功能丰富的库,如果感兴趣,可以阅读查看 Julia’s Random documentation 了解更多信息。 接下来 讨论三个函数: randrandnseed!

        在开始前,首先导入 Random 模块。 先精确地导入想使用的方法:

        1. using Random: seed!
        • rand: 在某种数据结构或类型的 元素 中做随机抽样。
        • randn: 在标准正态分布(平均值 0 和标准差 1)中做随机抽样。

        3.5.2.1 rand

        默认情况下,可以不带参数地调用 rand ,那么它就会返回一个位于区间 \([0, 1)\) 的 Float64 随机数,该区间表示随机数的范围是 0 (包含)到 1 (排除)之间:

        1. rand()
        1. 0.5948892232969126

        可以使用多种方式更新 rand 的参数。 假设,想要获得多个随机数:

        1. rand(3)
        1. [0.6624302616592804, 0.5298519928354318, 0.6566323506396953]

        或者,想在不同的区间抽样:

        1. rand(1.0:10.0)
        1. 6.0

        也可以给区间指定步长,甚至可以在不同的类型间抽样。 这里使用不带点 . 的数字,所以 Julia 会将它们解释为 Int64 而不是 Float64

        1. rand(2:2:20)
        1. 2

        还可以组合和匹配参数:

        1. rand(2:2:20, 3)
        1. [14, 4, 8]

        它还支持元素集构成的元组:

        1. rand((42, "Julia", 3.14))
        1. 42

        也支持数组:

        1. rand([1, 2, 3])
        1. 3

        还可以是 Dict

        1. rand(Dict(:one => 1, :two => 2))
        1. :two => 2

        最后要讨论的 rand 参数选项是,使用数字元组指定随机数的维度。 如果执行此操作,那么返回类型将会变为数组。 例如,如下是由 1.0-3.0 间的 Float64 随机数构成的 2x2 矩阵:

        1. rand(1.0:3.0, (2, 2))
        1. 2×2 Matrix{Float64}:
        2. 3.0 1.0
        3. 3.0 2.0

        3.5.2.2 randn

        randn 遵循与 rand 相同的生成原理,但现在它只返回从 标准正态分布 中生成的随机数。 标准正态分布是平均值为 0 和标准差为 1 的正态分布。 其默认类型为 Float64,并且只接受 AbstractFloatComplex 的子类型:

        1. randn()
        1. 0.15198409671103658

        可以仅指定大小:

        1. randn((2, 2))
        1. 2×2 Matrix{Float64}:
        2. 0.726461 -0.802142
        3. 0.887722 1.05791

        3.5.2.3 seed!

        Random 概述的结尾部分, 我们接下来讨论 重现性。 我们经常需要让某些事能够 可复现。 这意味着,随机数生成器要每次 生成相同的随机数序列。 这可以通过 seed! 函数实现:

        1. seed!(123)
        2. rand(3)
        1. [0.521213795535383, 0.5868067574533484, 0.8908786980927811]
        1. seed!(123)
        2. rand(3)
        1. [0.521213795535383, 0.5868067574533484, 0.8908786980927811]

        在一些例子中,在脚本开头调用 seed! 是不够好的。 为了避免 randrandn 依赖全局变量,那么可以转而定义一个 seed! 的实例,然后将它传递给 randrandn 的第一个参数。

        1. my_seed = seed!(123)
        1. Random.TaskLocalRNG()
        1. rand(my_seed, 3)
        1. [0.521213795535383, 0.5868067574533484, 0.8908786980927811]
        1. randn(my_seed, 3)
        1. [-0.21766510678354617, 0.4922456865251828, 0.9809798121241488]

        最后一个要讨论的 Julia 标准库是 Downloads 模块。 这部分相当简短,因为只关注单个函数 download

        假设想要 从互联网上下载文件到本地。 这可以用通过 download 函数实现。 最简单情况下,仅仅需要一个参数,那就是文件的 url。 还可以指定第二个参数作为下载文件的输出路径 (不要忘记文件系统的最佳实践!)。 如果不指定第二个参数,默认情况下,Julia 将使用 tempfile 函数创建一个临时文件。

        首先导入 Downloads 模块:

        1. using Downloads

        例如,下载 的 Project.toml 文件。 注意,Downloads 模块并没有导出 download 函数,因此需要使用语法 Module.function 。 默认情况下, 它返回一个包含下载文件本地路径的字符串:

        1. url = "https://raw.githubusercontent.com/JuliaDataScience/JuliaDataScience/main/Project.toml"
        2. my_file = Downloads.download(url) # tempfile() being created
        1. /tmp/jl_rxAWj4

        可以使用 readlines 查看下载文件的前四行:

        1. 4-element Vector{String}:
        2. "name = \"JDS\""
        3. "uuid = \"6c596d62-2771-44f8-8373-3ec4b616ee9d\""