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.
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):
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:
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
edf
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ávelname
só deveriam ser acessados viaDataFrame
! As variáveisname
egrade_2020
não foram feitas para serem mantidas por muito tempo! Agora, imagine se mudássemos o conteúdo degrade_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()
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 DataFrame
s. 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
.