Julia tem uma biblioteca padrão rica que está disponível em toda instalação de Julia. Ao contrário de tudo o que vimos até agora, por exemplo, tipos, estruturas de dados e sistema de arquivos; você deve carregar módulos de biblioteca padrão em seu ambiente para usar um módulo ou função particular.
Isso é feito via using
ou import
. Neste livro, carregaremos o código via using
:
using ModuleName
Depois de fazer isso, você pode acessar todas as funções e tipos dentro do módulo chamado ModuleName
.
Saber como lidar com datas e timestamps é importante na ciência de dados. Como dissemos na seção Por que Julia? (Section 2), o pandas
do Python usa seu próprio tipo de datetime
para lidar com datas. O mesmo é verdade no tidyverse de R, no pacote lubridate
, que também define o seu próprio tipo de datetime
para lidar com datas. Em Julia, os pacotes não precisam escrever sua própria lógica de datas, porque Julia tem um módulo de datas em sua biblioteca padrão chamado Dates
.
Para começar, vamos carregar o módulo Dates
:
using Dates
Date
e DateTime
O módulo de biblioteca padrão Dates
tem dois tipos para trabalhar com datas:
Date
: representando o tempo em dias eDateTime
: representando o tempo com precisão de milisegundos.Nós podemos construir Date
e DateTime
com o construtor padrão especificando um número inteiro para representar ano, mês, dia, horas e assim por diante:
Date(1987) # year
1987-01-01
Date(1987, 9) # year, month
1987-09-01
Date(1987, 9, 13) # year, month, day
1987-09-13
DateTime(1987, 9, 13, 21) # year, month, day, hour
1987-09-13T21:00:00
DateTime(1987, 9, 13, 21, 21) # year, month, day, hour, minute
1987-09-13T21:21:00
Para os curiosos, 13 de setembro de 1987, 21:21 é a hora oficial do nascimento do primeiro autor, José.
Nós também podemos passar tipos “período” ou Period
para o construtor padrão. Tipos Period
são o equivalente-humano para a representação do tempo para o computador. Dates
em Julia têm os seguintes subtipos abstratos de Period
:
subtypes(Period)
DatePeriod
TimePeriod
que se dividem nos seguintes tipos concretos, e eles são bastante autoexplicativos:
subtypes(DatePeriod)
Day
Month
Quarter
Week
Year
subtypes(TimePeriod)
Hour
Microsecond
Millisecond
Minute
Nanosecond
Second
Assim, poderíamos, alternativamente, construir a hora oficial de nascimento de José como:
DateTime(Year(1987), Month(9), Day(13), Hour(21), Minute(21))
1987-09-13T21:21:00
Na maioria das vezes, não construiremos instâncias Date
ou DateTime
do zero. Na verdade, nós provavelmente parsearemos strings para transformá-las em tipos Date
ou DateTime
.
Os construtores Date
e DateTime
podem ser alimentados com uma string e uma string de formato de data. Por exemplo, a string "19870913"
representando 13 de setembro de 1987 pode ser parseada com:
Date("19870913", "yyyymmdd")
1987-09-13
Observe que o segundo argumento é uma representação em string do formato. Temos os primeiros quatro dígitos que representam o ano y
, seguido por dois dígitos para o mês m
e finalmente dois dígitos para dia d
.
Também funciona para timestamps com DateTime
:
DateTime("1987-09-13T21:21:00", "yyyy-mm-ddTHH:MM:SS")
1987-09-13T21:21:00
Você pode encontrar mais informações sobre como especificar diferentes formatos de data na documentação Dates
’ de Julia. Não se preocupe se você tiver que revisitá-lo o tempo todo, nós mesmos fazemos isso ao trabalhar com datas e timestamps.
De acordo com a documentação Dates
’ de Julia, usar o método Date(date_string, format_string)
é satisfatório se ele for chamado apenas algumas vezes. Se houver muitas strings de data formatadas de forma semelhante para analisar, no entanto, é muito mais eficiente criar primeiro um tipo DateFormat
, e, em seguida, o passar em vez de uma string de formato bruto. Então, nosso exemplo anterior se torna:
format = DateFormat("yyyymmdd")
Date("19870913", format)
1987-09-13
Como alternativa, sem perda de desempenho, você pode usar o prefixo de string literal dateformat"..."
:
Date("19870913", dateformat"yyyymmdd")
1987-09-13
É fácil extrair as informações desejadas dos objetos Date
eDateTime
. Primeiro, vamos criar uma instância de uma data muito especial:
my_birthday = Date("1987-09-13")
1987-09-13
Podemos extrair tudo o que quisermos de my_birthday
:
year(my_birthday)
1987
month(my_birthday)
9
day(my_birthday)
13
O módulo Dates
de Julia também tem funções compostas que retornam uma tupla de valores:
yearmonth(my_birthday)
(1987, 9)
monthday(my_birthday)
(9, 13)
yearmonthday(my_birthday)
(1987, 9, 13)
Também podemos ver o dia da semana e outras coisas úteis:
dayofweek(my_birthday)
7
dayname(my_birthday)
Sunday
dayofweekofmonth(my_birthday)
2
Sim, José nasceu no segundo domingo de setembro.
OBSERVAÇÃO: Aqui está uma dica útil para recuperar apenas os dias de semana de instâncias de
Dates
. Use ofilter
nodayofweek(your_date) <= 5
. Para o dia útil, você pode verificar o pacoteBusinessDays.jl
.
Podemos realizar operações em instâncias de Dates
. Por exemplo, podemos adicionar dias a uma instância Date
ou DateTime
. Note que as Dates
em Julia executará automaticamente os ajustes necessários para anos bissextos, e por meses com 30 ou 31 dias (isso é conhecido como aritmética calendárica.
my_birthday + Day(90)
1987-12-12
Podemos adicionar quantas quisermos:
my_birthday + Day(90) + Month(2) + Year(1)
1989-02-11
Caso você esteja se perguntando: “O que posso fazer com datas mesmo? O que está disponível?” então você pode usar methodswith
para verificar. Mostramos apenas os primeiros 20 resultados aqui:
first(methodswith(Date), 20)
[1] show(io::IO, dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/io.jl:707
[2] show(io::IO, ::MIME{Symbol("text/plain")}, dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/io.jl:705
[3] DateTime(dt::Date, t::Time) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/types.jl:403
[4] Day(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
[5] Month(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
[6] Quarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
[7] Week(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
[8] Year(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/periods.jl:36
[9] firstdayofmonth(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:84
[10] firstdayofquarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:157
[11] firstdayofweek(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:52
[12] firstdayofyear(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:119
[13] lastdayofmonth(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:100
[14] lastdayofquarter(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:180
[15] lastdayofweek(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:68
[16] lastdayofyear(dt::Date) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/adjusters.jl:135
[17] +(dt::Date, t::Time) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:19
[18] +(dt::Date, y::Year) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:27
[19] +(dt::Date, z::Month) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:54
[20] +(x::Date, y::Quarter) in Dates at /opt/hostedtoolcache/julia/1.7.1/x64/share/julia/stdlib/v1.7/Dates/src/arithmetic.jl:73
A partir disso, podemos concluir que também podemos usar o operador de sinal de mais +
e menos -
. Vamos ver quantos anos o José tem, em dias:
today() - my_birthday
12556 days
A duração padrão de tipos de Date
é uma instância de Day
. Para o DateTime
, a duração padrão é uma instância de Millisecond
:
DateTime(today()) - DateTime(my_birthday)
1084838400000 milliseconds
Uma coisa boa sobre o módulo Dates
é que também podemos construir facilmente intervalos de data e hora. Julia é inteligente o suficiente para não ter que definir todos os tipos de intervalo e operações que abordamos em Section 3.2.5. Ela apenas estende as funções e operações definidas para range para os tipos Date
. Isso é conhecido como despacho múltiplo e já abordamos isso em Por que Julia? (Section 2).
Por exemplo, suponha que você deseja criar um intervalo de tipo Day
. Isso é fácil de fazer com o operador dois-pontos :
:
Date("2021-01-01"):Day(1):Date("2021-01-07")
2021-01-01
2021-01-02
2021-01-03
2021-01-04
2021-01-05
2021-01-06
2021-01-07
Não há nada de especial em usar Day(1)
como o intervalo, podemos usar qualquer tipo Period
como intervalo. Por exemplo, usando 3 dias como intervalo:
Date("2021-01-01"):Day(3):Date("2021-01-07")
2021-01-01
2021-01-04
2021-01-07
Ou mesmo meses:
Date("2021-01-01"):Month(1):Date("2021-03-01")
2021-01-01
2021-02-01
2021-03-01
Perceba que o tipo deste intervalo é um StepRange
com o Date
e um tipo concreto Period
que usamos como intervalo dentro do operador :
:
date_interval = Date("2021-01-01"):Month(1):Date("2021-03-01")
typeof(date_interval)
StepRange{Date, Month}
Podemos converter isso para um vetor com a função collect
:
collected_date_interval = collect(date_interval)
2021-01-01
2021-02-01
2021-03-01
E teremos todas as funcionalidades de array disponíveis, como, por exemplo, indexação:
collected_date_interval[end]
2021-03-01
Também podemos fazer broadcast de operações de data em um vetor de Date
s:
collected_date_interval .+ Day(10)
2021-01-11
2021-02-11
2021-03-11
Da mesma forma, esses exemplos funcionam para tipos DateTime
também.
Outro módulo importante na biblioteca padrão de Julia é o módulo Random
. Este módulo lida com geração de números aleatórios. Random
é uma biblioteca rica e, se você está interessado, deve consultar a documentação Random
de Julia. Vamos cobrir somente três funções: rand
, randn
e seed!
.
Para começar, primeiro carregamos o módulo Random
. Como sabemos exatamente o que queremos carregar, podemos também carregar explicitamente os métodos que queremos usar:
using Random: rand, randn, seed!
Nós temos duas funções principais que geram números aleatórios:
rand
: faz a amostragem de um elemento aleatório de uma estrutura ou tipo de dados.randn
: gera um número aleatório que segue uma distribuição normal padrão (média 0 e desvio padrão 1) de um tipo específico.OBSERVAÇÃO: Observe que essas duas funções já estão no módulo
Base
de Julia. Então, você não precisa importarRandom
se estiver planejando usá-los.
rand
Por padrão, se você chamar rand
sem argumentos, ele retornará um Float64
no intervalo \([0, 1)\), o que significa no intervalo compreendido entre os limites 0 inclusivo e 1 exclusivo:
rand()
0.9254222481238623
Você pode modificar argumentos rand
de várias maneiras. Por exemplo, suponha que você queira mais de 1 número aleatório:
rand(3)
[0.7029244663701457, 0.6589159413712516, 0.7637832268559482]
Ou você quer um intervalo diferente:
rand(1.0:10.0)
9.0
Você também pode especificar um tamanho de incremento diferente dentro do intervalo e um tipo diferente. Aqui estamos usando números sem ponto .
então Julia irá interpretá-los como Int64
:
rand(2:2:20)
20
Você também pode misturar e combinar argumentos:
rand(2:2:20, 3)
[14, 20, 20]
rand
também aceita uma coleção de elementos como, por exemplo, uma tupla:
rand((42, "Julia", 3.14))
42
E também arrays:
rand([1, 2, 3])
2
Dict
s:
rand(Dict(:one => 1, :two => 2))
:two => 2
Para terminar todas as opções de argumentos rand
, você pode especificar as dimensões de número aleatório desejadas em uma tupla. Se você fizer isso, o tipo retornado será uma array. Por exemplo, aqui uma matriz 2x2 de números Float64
entre 1.0 e 3.0:
rand(1.0:3.0, (2, 2))
2×2 Matrix{Float64}:
1.0 2.0
2.0 3.0
randn
randn
segue o mesmo princípio geral de rand
, mas agora ele só retorna números gerados a partir da distribuição normal padrão. A distribuição normal padrão é a distribuição normal com média 0 e desvio padrão 1. O tipo padrão é Float64
e só permite subtipos de AbstractFloat
ou Complex
:
randn()
0.513588244203061
Só podemos especificar o tamanho:
randn((2, 2))
2×2 Matrix{Float64}:
0.147726 0.33378
-0.166356 -0.268258
seed!
Para terminar a visão geral de Random
, vamos falar sobre reprodutibilidade. Muitas vezes, queremos fazer algo replicável. Ou seja, queremos que o gerador de números aleatórios gere a mesma sequência aleatória de números. Podemos fazer isso com a função seed!
:
seed!(123)
rand(3)
[0.521213795535383, 0.5868067574533484, 0.8908786980927811]
seed!(123)
rand(3)
[0.521213795535383, 0.5868067574533484, 0.8908786980927811]
A fim de evitar a repetição tediosa e ineficiente de seed!
em todo lugar, podemos definir uma instância de seed!
e passá-la como o primeiro argumento de rand
ou randn
.
my_seed = seed!(123)
Random.TaskLocalRNG()
rand(my_seed, 3)
[0.19090669902576285, 0.5256623915420473, 0.3905882754313441]
rand(my_seed, 3)
[0.19090669902576285, 0.5256623915420473, 0.3905882754313441]
OBSERVAÇÃO: Se você quiser que seu código seja reproduzível, basta chamar
seed!
no começo do seu script. Isso cuidará da reprodutibilidade sequencial das operaçõesRandom
. Não há necessidade de usá-la dentro de todorand
erandn
.
Uma última coisa da biblioteca padrão de Julia para cobrirmos é o módulo Download
. Será muito breve porque iremos cobrir apenas uma única função chamada download
.
Suponha que você queira fazer o download de um arquivo da internet para o seu armazenamento local. Você pode fazer isso com a função download
. O primeiro e único argumento obrigatório é o url do arquivo. Você também pode especificar como um segundo argumento o caminho de saída desejado para o arquivo baixado (não se esqueça das práticas recomendadas do sistema de arquivos!). Se você não especificar um segundo argumento, Julia irá, por padrão, criar um arquivo temporário com a função tempfile
.
Vamos carregar o método download
:
using Download: download
Por exemplo, vamos baixar nosso arquivo Project.toml
do repositório GitHub JuliaDataScience
. Observe que a função download
não é exportada pelo módulo Downloads
, então temos que usar a sintaxe Module.function
. Por padrão, ele retorna uma string que contém o caminho do arquivo para o arquivo baixado:
url = "https://raw.githubusercontent.com/JuliaDataScience/JuliaDataScience/main/Project.toml"
my_file = Downloads.download(url) # tempfile() being created
/tmp/jl_ZKhQ5U
Com readlines
, nós podemos observar as primeiras 4 linhas do arquivo que baixamos:
readlines(my_file)[1:4]
4-element Vector{String}:
"name = \"JDS\""
"uuid = \"6c596d62-2771-44f8-8373-3ec4b616ee9d\""
"authors = [\"Jose Storopoli\", \"Rik Huijzer\", \"Lazaro Alonso\"]"
"version = \"0.1.0\""
OBSERVAÇÃO: Para interações HTTP mais complexas, como interação com APIs da web, consulte o pacote
HTTP.jl
.