4 DataFrames.jl

Em geral, os dados vêm em formato tabular. Por tabular, queremos dizer que os dados consistem em uma tabela que contém linhas e colunas. As colunas normalmente contêm o mesmo tipo de dados, enquanto as linhas são de tipos diferentes. As linhas, na prática, denotam observações enquanto as colunas indicam variáveis. Por exemplo, podemos ter uma tabela de programas de TV contendo o país em que cada um foi produzido e nossa classificação pessoal, acesse Table 1.

Table 1: TV shows.
name country rating
Game of Thrones United States 8.2
The Crown England 7.3
Friends United States 7.8

Aqui, as reticências significam que esta pode ser uma tabela muito longa e mostramos apenas algumas linhas. Ao analisar dados, muitas vezes levantamos questões interessantes sobre eles, também chamadas de queries (ou consultas) de dados. Para tabelas grandes, os computadores são capazes de responder a perguntas desse tipo muito mais rápido do que você faria manualmente. Alguns exemplos de questões para os dados seriam:

Mas, como pesquisador, você verá que a ciência real muitas vezes começa com várias tabelas ou fontes de dados. Por exemplo, se também tivéssemos dados das classificações de outra pessoa para os programas de TV (Table 2):

Table 2: Ratings.
name rating
Game of Thrones 7
Friends 6.4

Agora, poderíamos fazer as seguintes perguntas:

Ao longo deste capítulo, mostraremos como você pode responder facilmente a essas perguntas em Julia. Para fazer isso, primeiro mostramos porque precisamos de um pacote Julia chamado DataFrames.jl. Nas próximas seções, mostraremos como você pode usar este pacote e também como escrever transformações de dados (Section 4.9).

Vejamos uma tabela de notas escolares como a Table 3:

Table 3: Grades for 2020.
name age grade_2020
Bob 17 5.0
Sally 18 1.0
Alice 20 8.5
Hank 19 4.0

Aqui, o nome da coluna tem um tipo string, idade tem um tipo integer e nota um tipo float.

Até agora, este livro tratou apenas do básico de Julia. Esse básico é bom para muitas coisas, mas não para tabelas. Para mostrar que precisamos de mais, vamos tentar armazenar os dados tabulares em arrays:

function grades_array()
    name = ["Bob", "Sally", "Alice", "Hank"]
    age = [17, 18, 20, 19]
    grade_2020 = [5.0, 1.0, 8.5, 4.0]
    (; name, age, grade_2020)
end

Agora, os dados são armazenados na chamada forma colunar, o que é complicado quando queremos obter dados de uma linha:

function second_row()
    name, age, grade_2020 = grades_array()
    i = 2
    row = (name[i], age[i], grade_2020[i])
end
second_row()
("Sally", 18, 1.0)

Ou ainda, se você quiser ver a nota de Alice, primeiro você precisa descobrir em que linha Alice está:

function row_alice()
    names = grades_array().name
    i = findfirst(names .== "Alice")
end
row_alice()
3

e então podemos obter o valor:

function value_alice()
    grades = grades_array().grade_2020
    i = row_alice()
    grades[i]
end
value_alice()
8.5

DataFrames.jl é capaz de resolver esse tipo de problemas com facilidade. Você pode começar carregando DataFrames.jl com using:

using DataFrames

Com DataFrames.jl, podemos definir uma DataFrame para armazenar nossos dados tabulares:

names = ["Sally", "Bob", "Alice", "Hank"]
grades = [1, 5, 8.5, 4]
df = DataFrame(; name=names, grade_2020=grades)
name grade_2020
Sally 1.0
Bob 5.0
Alice 8.5
Hank 4.0

o que nos dá uma variável df que contém nossos dados em formato de tabela.

OBSERVAÇÃO: Isso funciona, mas há uma coisa que precisamos mudar imediatamente. Neste exemplo, definimos as variáveis name, grade_2020 e df em escopo global. Isso significa que essas variáveis podem ser acessadas e editadas de qualquer lugar. Se continuássemos escrevendo o livro assim, teríamos algumas centenas de variáveis no final do livro, embora os dados que colocamos na variável name só deveriam ser acessados via DataFrame! As variáveis name e grade_2020 não foram feitas para serem mantidas por muito tempo! Agora, imagine se mudássemos o conteúdo de grade_2020 algumas vezes nesse livro. Dado apenas o livro como PDF, seria quase impossível descobrir o conteúdo da variável ao final.

Podemos resolver isso facilmente usando funções.

Vamos fazer a mesma coisa de antes, mas agora em uma função:

function grades_2020()
    name = ["Sally", "Bob", "Alice", "Hank"]
    grade_2020 = [1, 5, 8.5, 4]
    DataFrame(; name, grade_2020)
end
grades_2020()
Table 4: Grades 2020.
name grade_2020
Sally 1.0
Bob 5.0
Alice 8.5
Hank 4.0

Note que name e grade_2020 são destruídas depois que a função retorna, ou seja, só estão disponíveis na função. Existem dois outros benefícios ao fazer isso. Primeiro, agora está claro para o leitor onde name e grade_2020 pertencem: eles pertencem às notas de 2020. Em segundo lugar, é fácil determinar qual seria a saída de grades_2020() em qualquer ponto do livro. Por exemplo, agora podemos atribuir os dados a uma variável df:

df = grades_2020()
name grade_2020
Sally 1.0
Bob 5.0
Alice 8.5
Hank 4.0

Podemos também mudar os conteúdos de df:

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

E ainda assim recuperar os dados originais de volta sem nenhum problema:

df = grades_2020()
name grade_2020
Sally 1.0
Bob 5.0
Alice 8.5
Hank 4.0

Claro, pressupondo que a função não seja redefinida. Prometemos não fazer isso neste livro, porque é uma má ideia exatamente por este motivo. Ao invés de “mudarmos” a função, vamos fazer uma nova e lhe daremos um nome claro.

Portanto, retornemos ao construtor DataFrames. Como você deve ter visto, a maneira de criar um é simplesmente passar vetores como argumentos para o construtor DataFrame. Você pode aparecer com qualquer vetor de Julia válido e ele funcionará contanto que os vetores tenham o mesmo comprimento. Valores duplicados, símbolos Unicode e qualquer tipo de número são aceitos:

DataFrame(σ = ["a", "a", "a"], δ = [π, π/2, π/3])
σ δ
a 3.141592653589793
a 1.5707963267948966
a 1.0471975511965976

Normalmente, em seu código, você criaria uma função que envelopa uma ou mais funções DataFrames. Por exemplo, podemos fazer uma função para obter as notas de um ou mais names:

function grades_2020(names::Vector{Int})
    df = grades_2020()
    df[names, :]
end
grades_2020([3, 4])
name grade_2020
Alice 8.5
Hank 4.0

Esta forma de usar funções para envelopar funcionalidades básicas em linguagens de programação e pacotes é bastante comum. Basicamente, você pode pensar em Julia e DataFrames.jl como peças de Lego. Eles fornecem peças muito genéricas que permitem que você crie coisas para seu caso de uso específico como neste exemplo de notas. Usando essas peças, você pode fazer um script de análise de dados, controlar um robô ou o que você quiser construir.

Até agora, os exemplos eram bastante complicados, porque tínhamos que usar índices. Nas próximas seções, mostraremos como carregar e salvar dados, e muitas outras peças de Lego poderosas fornecidas por DataFrames.jl.



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