3.4 Biblioteca Padrão de Julia

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.

3.4.1 Datas

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

3.4.1.1 Tipos Date e DateTime

O módulo de biblioteca padrão Dates tem dois tipos para trabalhar com datas:

  1. Date: representando o tempo em dias e
  2. DateTime: 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

3.4.1.2 Parseando Datas

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

3.4.1.3 Extraindo Informações de Data

É 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 o filter no dayofweek(your_date) <= 5. Para o dia útil, você pode verificar o pacote BusinessDays.jl.

3.4.1.4 Operações de Data

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

3.4.1.5 Intervalos de Data

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 Dates:

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.

3.4.2 Números Aleatórios

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:

OBSERVAÇÃO: Observe que essas duas funções já estão no módulo Base de Julia. Então, você não precisa importar Random se estiver planejando usá-los.

3.4.2.1 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

Dicts:

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

3.4.2.2 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

3.4.2.3 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ções Random. Não há necessidade de usá-la dentro de todo rand e randn.

3.4.3 Downloads

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.



CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso