Pacote TidierIteration.jl

Julia
Data Science
Tidyverse

Saudades do pacote purrr do R? Conheça o TidierIteration.jl, a implementação 100% Julia que traz map, walk, flatten e outras ferramentas funcionais para seus pipelines de dados.

Autor
Afiliação

Universidade Estadual de Campinas

Data de Publicação

19 de abril de 2026

Introdução

Se você vem do universo R, provavelmente tem um lugar especial no coração para o pacote purrr. A capacidade de mapear funções, iterar sobre listas complexas e limpar dados de forma funcional é simplesmente viciante.

Embora Julia tenha ferramentas nativas poderosas (como o broadcasting f.(x) e compreensões de lista), às vezes sentimos falta daquela sintaxe fluida orientada a pipelines (|>).

É aqui que entra o TidierIteration.jl.

O que é?

O TidierIteration.jl é uma implementação em Julia do purrr. Ele visa simplificar o mapeamento de funções para Arrays, Dicionários e outras estruturas, além de fornecer advérbios (possibly, quietly) e funções de “achatamento” (flatten) essenciais para lidar com dados aninhados (como JSON).

Instalação

Caso ainda não tenha o pacote TidierIteration.jl instalado, basta copiar e colar o código abaixo.

import Pkg;
Pkg.add("TidierIteration")

Neste post, utilizaremos, além do pacote TidierIteration.jl, também o pacote Plots.

using Plots
using TidierIteration

Por que usar TidierIteration.jl se já existe map?

Julia já possui map(f, x). O TidierIteration.jl introduz a função map_tidy(), que coloca os dados como primeiro argumento. Isso padroniza a leitura do código.

Por exemplo, imagine que queremos elevar números ao quadrado e subtrair 1.

Jeito nativo:

dados = [1, 2, 3, 4, 5];
map(x -> x^2 - 1, dados)
5-element Vector{Int64}:
  0
  3
  8
 15
 24

Com TidierIteration.jl (usando o pipe |>):

# A função map_tidy recebe os dados primeiro, depois a função
dados |> d ->
    map_tidy(d, x -> x^2 - 1)
5-element Vector{Int64}:
  0
  3
  8
 15
 24

Lidando com Dicionários

Iterar sobre dicionários em Julia nativo pode ser um pouco verboso. O pacote facilita isso com map_keys() e map_values().

precos = Dict("Arroz" => 20.50, "Feijão" => 8.90, "Café" => 35.00)
Dict{String, Float64} with 3 entries:
  "Feijão" => 8.9
  "Arroz"  => 20.5
  "Café"   => 35.0

# Aplicando desconto de 10% apenas nos valores
precos |>
    d -> map_values(d, v -> v * 0.9) |>
    println
Dict("Feijão" => 8.01, "Arroz" => 18.45, "Café" => 31.5)

Lidando com falhas: possibly

Uma das features mais amadas do purrr são os “advérbios” que modificam funções. O possibly permite que seu código continue rodando mesmo se encontrar um erro, retornando um valor padrão.

# Lista com um erro proposital (string no meio de números)
lista_suja = [10, 20, "erro", 40];

# Função perigosa
dividir_por_2(x) = x / 2
dividir_por_2 (generic function with 1 method)

# Criando uma versão segura da função
# Se der erro, retorna missing
dividir_seguro = possibly(dividir_por_2, missing)
(::TidierIteration.var"#f2#27"{TidierIteration.var"#f2#26#28"{Bool, typeof(dividir_por_2), Missing}}) (generic function with 1 method)

# Agora podemos rodar sem medo de quebrar o loop
lista_suja |>
    d -> map_tidy(d, dividir_seguro) |>
    println
Union{Missing, Float64}[5.0, 10.0, missing, 20.0]

Isso é vital em web scraping ou leitura de arquivos sujos!

Efeitos colaterais com walk

Às vezes, queremos executar uma ação (como imprimir na tela ou salvar um arquivo) sem alterar os dados que estão passando pelo pipe.

Enquanto o map() retorna o resultado da função (o que quebraria o pipe se a função retornasse nothing), o walk() executa a ação e devolve o objeto original.

Imagine que queremos criar gráficos para várias funções e salvá-los automaticamente no computador:

# Lista de funções matemáticas para plotar
funcoes = [sin, cos, exp];

# Vamos criar os gráficos na memória
# Retorna: Um vetor de objetos Plot
meus_plots =
    funcoes |>
    fns -> map_tidy(fns, f ->
        plot(f, 0, 2π,
             title = string(f),
             label = false)
    )
3-element Vector{Plots.Plot{Plots.GRBackend}}:
 Plot{Plots.GRBackend() n=1}
 Plot{Plots.GRBackend() n=1}
 Plot{Plots.GRBackend() n=1}

# Agora usamos iwalk para exibir cada um.
# iwalk recebe (elemento, indice) na ordem do Julia (mas no Tidier é invertido: indice, elemento)
meus_plots |>
    ps -> iwalk(ps, (i, p) -> display(p));
meus_plots[1]

meus_plots[2]

meus_plots[3]

Cuidado! Por que não usar map aqui?

Se tentássemos usar imap no lugar de iwalk, perderíamos nossos dados. A função display retorna nothing. O map captura esse retorno e substitui nossos gráficos por ele.

Veja a demonstração do erro:

# TENTATIVA ERRADA: Usando 'imap' para exibir
# O imap vai retornar uma lista com o resultado de display() (que é nothing)
resultado_quebrado =
    meus_plots |>
    ps -> imap(ps, (i, p) -> display(p))
println("Tipo do objeto após imap: ", typeof(resultado_quebrado))
Tipo do objeto após imap: Vector{Nothing}
println("Conteúdo final: ", resultado_quebrado)
Conteúdo final: [nothing, nothing, nothing]

Como resultado, resultado_quebrado vira um vetor de nothing’s, e o pipeline morre ali. Com walk(), os dados originais (meus_plots) são preservados e passados adiante.

Domando JSONs com flatten

Quem trabalha com APIs sabe o pesadelo que é lidar com listas de listas de dicionários. O flatten_*() resolve isso.

# Dados aninhados simulando uma resposta JSON
dados_api = [
    Dict("id" => 1, "info" => Dict("nome" => "João", "score" => 10)),
    Dict("id" => 2, "info" => Dict("nome" => "Maria", "score" => 20))
]
2-element Vector{Dict{String, Any}}:
 Dict("id" => 1, "info" => Dict{String, Any}("score" => 10, "nome" => "João"))
 Dict("id" => 2, "info" => Dict{String, Any}("score" => 20, "nome" => "Maria"))

# A função 'flatten' funciona em um dicionário por vez.
# Então usamos map_tidy para aplicar o flatten em cada item da lista.
dados_api |>
    d -> map_tidy(d, flatten) |>
    fd -> println(fd[1])
Dict{String, Any}("id" => 1, "info_nome" => "João", "info_score" => 10)

# Apenas demonstrando a estrutura simplificada do primeiro item
# Veja que "info" sumiu e virou "info_nome" e "info_score"

Conclusão

O TidierIteration.jl preenche uma lacuna importante para cientistas de dados que buscam uma sintaxe consistente, legível e orientada a pipelines. Se você trabalha com dados hierárquicos complexos ou apenas sente falta do fluxo de trabalho do purrr, vale a pena testar!

Veja também os nossos outros blogs sobre os outros pacotes da biblioteca Tidier.jl!

Referências

Nota

Ferramentas de IA foram utilizadas para correção ortográfica, organização estrutural e aprimoramento da clareza do texto.