No início, descreveremos resumidamente como era o processo antes e depois de ensinarmos nosso produto interno A1 a coletar fontes de dados programaticamente e publicá-las no Tableau Server. Em seguida, examinaremos mais de perto o problema do comando BI e a solução encontrada, e também daremos uma olhada nos bastidores (aqui sobre como criar um arquivo .hyper, publicar um arquivo em um servidor tableau e atualizar um hyper). Bem-vindo ao gato!

Nós do grupo de publicidade DAN trabalhamos muito com dados de monitoramento de publicidade do Mediascope , um medidor industrial do mercado de mídia. Existem diferentes cenários: alguns funcionários carregam dados brutos, outros usam bancos de dados pré-processados prontos e alguém ordena o desenvolvimento de painéis automatizados com base nesses dados. Vamos falar sobre o último cenário com mais detalhes - nossos desenvolvedores de BI coletam painéis no Tableau, mas antes de começar a "desenhar", eles também precisam trazer os dados para o formato desejado que seja conveniente para o desenvolvimento.
O percurso de vida dos dados, desde as matérias-primas até os belos gráficos automatizados, pode ser dividido em 4 etapas:
- Obtendo dados brutos
- Limpeza e revisão de dados
- Criação de fontes de dados para Tableau
- Desenvolvimento de visualizações
isso foi
Antes de aprendermos como gerar fontes de dados de maneira programática para o Tableau, o processo era assim:

1. Obtendo dados brutos
Os usuários geram relatórios tabulares por meio da ferramenta interna A1. Falaremos sobre isso com mais detalhes a seguir.
2. Limpeza e modificação de dados A
capacidade de transformação de dados também está incluída na ferramenta A1, após a qual os dados limpos podem ser carregados para xslx / csv e continuar a trabalhar com eles fora da ferramenta. É importante destacar aqui que alguns usuários se limitam ao primeiro ponto e, após o upload dos relatórios, modificam os dados por conta própria.
3. Criação de fontes de dados para o Tableau
Anteriormente, os clientes do painel vinham com um conjunto de Excels, que eles geraram nos parágrafos anteriores. E os desenvolvedores de BI trouxeram esses ex-works em uma única fonte de dados (gíria de tablóide) por conta própria. Nem sempre foi possível nos limitarmos apenas às ferramentas do Tableau; elas geralmente escreviam scripts em Python.
4. Desenvolvimento de visualizações
Por fim, a ponta do iceberg é criar um painel e publicá-lo no Tableau Server, onde o cliente pode vê-lo. Na prática, a renderização geralmente leva menos tempo do que a coleta de dados e a configuração de atualizações.
A dor se acumulou na terceira etapa, conforme crescia o número de soluções personalizadas, cuja manutenção e implementação eram caras. Além disso, erros nos dados da segunda etapa vazavam regularmente - o Excel intermediário entre os dois sistemas (A1 e Tableau) pressionava o usuário: “vamos consertar algo com canetas, ninguém vai notar”.
Se tornou
A principal tarefa era eliminar ex-peles entre 2 e 3 etapas. Como resultado, ensinamos A1 a coletar fontes de dados e publicá-las no Tableau Server. Eis o que aconteceu:

Agora, as etapas 1 a 3 ocorrem em A1, na saída a equipe de BI recebe uma fonte de dados publicada no Tableau Server para desenvolver visualizações. A Hyper API tornou-se o link de conexão, que será discutido mais adiante.
resultados
Reduziu o número de nós ao trabalhar em ferramentas diferentes. Agora é mais difícil cometer um erro em algum ponto do processo e é mais fácil detectar onde o erro ocorreu, a investigação de falhas leva menos tempo. O sistema avisa os usuários sobre erros comuns.
Libere tempo para a equipe de BI . Anteriormente, havia poucas soluções de modelo e muitas personalizações. Na maioria das vezes, o processamento em Python foi adicionado para cada projeto. Em casos raros, nos quais o processamento não era necessário, trabalhamos diretamente no Tableau Desktop (a principal ferramenta de desenvolvimento).
Agora a preparação da fonte de dados é: clicar nos campos obrigatórios na interface A1, marcar quais deles expandimos em linhas (se necessário) e opcionalmente definir o tipo de campos com antecedência.
Não carregamos o Tableau Serveratualização de fontes de dados volumosas - a atualização é feita por A1, e um hiper pronto é baixado para o servidor.
* Bônus - encorajamos os usuários a trabalhar dentro do A1. Se antes alguns usuários, após descarregar os relatórios brutos, os modificavam manualmente fora da ferramenta, agora, como todo o processo das etapas 1 a 3 ocorre em A1, é mais fácil para os usuários configurar o processo de limpeza ali.
Problema e solução
Um pouco sobre A1
Antes de começarmos a falar sobre nossa solução, precisamos falar sobre nosso produto interno A1, ao qual anexamos a geração de fontes de dados.
A1 é um produto interno da empresa, que se destina a simplificar o fluxo de trabalho dos funcionários cuja função principal é a seguinte:
- Recupere dados de produtos de software MediaScope
- Traga (limpe) esses dados em uma forma conveniente para analistas de assunto
- Se necessário, prepare os dados para a criação de painéis (falaremos sobre isso hoje)
Depois que o usuário termina de limpar os dados, eles são armazenados no sistema A1. Em nossa terminologia, isso é chamado de "Container". Um contêiner é um documento normal no MongoDB, que precisamos transferir para o servidor Tableau.
O problema da equipe de BI
Nossa equipe de desenvolvimento de BI precisava, de alguma forma, obter dados de A1, que estavam armazenados no MongoDB, e construir painéis com base nos dados recebidos. Em primeiro lugar, tentamos obter dados do MongoDB usando ferramentas de placar regulares, mas isso não resolveu o problema:
- Como os dados são armazenados no MongoDB, os dados com uma estrutura arbitrária são recebidos na entrada do placar, o que significa que você teria que manter essa lógica constantemente.
- Para agregar dados do MongoDB, era necessário arrastar certos registros da coleção, e não a coleção inteira - o driver do Tableau não pode fazer isso.
- Entre outras coisas, não era suficiente obter os dados: às vezes, eles tinham que ser "expandidos" - para "desfiar" algumas colunas em linhas. O que, também, não era tão fácil de fazer, de jeito nenhum.
O que nós descobrimos
Decidimos tentar resolver esse problema com minha bicicleta, usando a biblioteca Tableau Hyper API . Esta biblioteca permite que você crie um arquivo no formato .hyper, no qual é fácil adicionar dados e, em seguida, use-o como uma fonte de dados para criar um painel na placa do servidor.
Conforme os desenvolvedores do placar descrevem o hiper:
Hyper é um mecanismo de dados na memória de alto desempenho que ajuda os clientes a analisar rapidamente conjuntos de dados grandes ou complexos, avaliando com eficiência as consultas ao banco de dados. Com base na plataforma Tableau, o Hyper usa técnicas proprietárias de geração de código dinâmico e tecnologias de simultaneidade avançadas para obter alto desempenho em extrações e consultas.O processo aproximado de trabalho em nosso programa é o seguinte:
- O usuário seleciona contêineres e colunas desejadas
- O sistema extrai dados de contêineres
- Com base nos dados recebidos, o sistema determina os tipos de colunas
- A criação de um hiper e a inserção de dados nele é inicializada
- O hiper é carregado no servidor de placar
- Os desenvolvedores de BI veem o hyper no servidor e criam um painel baseado nele
Quando novos dados forem colocados nos contêineres, o sistema receberá um sinal de que é necessário atualizar o hiper:
- O sistema irá baixar o hyper do servidor de placar
- Pegará dados novos do MongoDB e atualizará o hyper
- Depois disso, o sistema carrega um novo hiper para o servidor, sobrescrevendo o existente.
- O usuário só precisa clicar no botão "Atualizar" para exibir informações atualizadas no painel
O que o usuário vê
Conforme declarado anteriormente, A1 é um aplicativo da web. Usamos Vue.js e Vuetify para criar um serviço de hiper-geração front-end.
A interface do aplicativo é dividida em três telas.
Na primeira tela, o usuário seleciona os containers e colunas desejados.
Se a opção "Unpivot" estiver habilitada, duas colunas adicionais serão criadas na hiper: variável - os nomes das colunas que são selecionadas pela coluna Metrics e os valores - os valores dessas colunas.
A coluna Dimensão adiciona uma coluna com a coluna selecionada de mesmo nome ao hiper. O número de colunas selecionadas Dimensões e seus nomes devem ser iguais em todos os contêineres para que a integridade da tabela no hiper não seja violada, portanto, existe uma coluna "Hiper nome", que permite especificar o nome da coluna selecionada, caso sejam nomeados de forma diferente nos contêineres.
Isso conclui o processo de configuração do hiper. O usuário só precisa ir para a segunda tela, clicar em “Criar hiper” e observar o andamento dos eventos nos logs.
A terceira tela contém configurações adicionais:
- Você pode ignorar as atualizações se não precisarmos que o sistema atualize automaticamente o hiper
- Você pode especificar um e-mail para enviar relatórios de atualização
- Você pode especificar manualmente o tipo de dados para a coluna de valores (usado apenas no modo não dinâmico): float, string ou determinado automaticamente pelo sistema (falaremos mais sobre os tipos)
- Você também pode especificar tipos de dados para colunas selecionadas em contêineres.
O que está sob o capô
A1 é escrito em Python. Para trabalhar com dados, usamos o Pandas e serializamos os dados dos pandas para conservar e armazená-los no MongoDB GridFS.
Quando um comando para criar um hiper é recebido, o sistema executa as seguintes operações:
- Descarrega todos os contêineres necessários do MongoDB e desserializa os dados em datafremes do pandas
- Prepara dados: deixa apenas as colunas necessárias nos dataframes, dá-lhes novos nomes, expande as tabelas se necessário via pandas.melt
- Se o usuário tiver definido o tipo de dados para as colunas, converta os dados em float32 ou string
- Após todo o trabalho preparatório com os dados, o sistema cria um arquivo via hyper api e envia o arquivo para o servidor de placar via tabcmd.
Vale a pena falar um pouco sobre os tipos de dados das colunas. Uma das características de armazenar dados em contêineres A1 é que os usuários não se preocupam com quais tipos atribuir às colunas, o pandas faz isso por eles perfeitamente: o sistema enfrenta calmamente situações em que números e valores de string estão presentes em uma coluna. Porém, o hiper não gosta disso: se você disser a ele que a coluna deve ser do tipo int, o sistema irá xingar ao tentar inserir qualquer coisa que não seja um inteiro. Portanto, decidiu-se usar apenas dois tipos de dados em hypers: string e float.
Então, descobrimos o princípio geral de trabalho, vamos falar sobre como trabalhar com o próprio hiper.
Criação de um arquivo .hyper
Para trabalhar com o Hyper API, você precisa instalar a biblioteca, você pode baixá-la do site oficial aqui . Existem também alguns bons exemplos de como trabalhar com esta ferramenta. Vamos indicar brevemente os pontos principais.
O próprio arquivo hyiper é uma espécie de banco de dados, algo que lembra o SQLite. Por meio da API, você pode acessar os dados usando a sintaxe SQL:
f"SELECT {escape_name('Customer ID')} FROM {escape_name('Customer')}"
Como nosso sistema é escrito em Python, também usaremos a biblioteca para a linguagem correspondente. Ao criar o arquivo, devemos especificar o nome do esquema, nome da tabela e colunas com tipos. O nome do esquema e a tabela devem ser chamados de “Extrair”, pois é nesse esquema com a tabela que o Tableau Server escala para extrair os dados dos livros.
with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
with Connection(
hyper.endpoint, self.fullpath_hyper, CreateMode.CREATE_AND_REPLACE
) as connection:
connection.catalog.create_schema("Extract")
main_table = TableName("Extract", "Extract")
example_table = TableDefinition(main_table)
Depois de criar a tabela, precisamos criar colunas e definir tipos. Como dissemos antes, nossos dados têm apenas dois tipos (float ou string), portanto, com base no tipo de colunas no dataframe, definimos isso para as colunas:
for column in dataframe.columns:
if dataframe[column].dtype.name in ("category", "object"):
example_table.add_column(TableDefinition.Column(column, SqlType.text()))
elif dataframe[column].dtype.name in ("float32"):
example_table.add_column(
TableDefinition.Column(column, SqlType.double())
)
connection.catalog.create_table(example_table)
Depois de criar a tabela, você pode inserir dados:
with Inserter(connection, example_table) as inserter:
for val in dataframe.values:
inserter.add_row(val.tolist())
inserter.execute()
Aqui, percorremos o dataframe linha por linha e acumulamos a lista com os valores via inserter.add_row () . Na verdade, existe uma função add_rows () na api do hyper , que pega uma lista de listas e já insere os valores. Por que isso não foi feito? Para salvar a RAM: para fornecer uma lista de listas de valores do dataframe, você precisa pedir ao pandas para fazer values.tolist () . E quando você tem 150 milhões de linhas de dados, acaba sendo uma operação muito cara para a RAM, embora isso não afete o desempenho de forma alguma (em qualquer caso, não foi notado que devido à iteração iterativa ao longo das linhas, a velocidade de criação de um hiper diminuiu de alguma forma). Além disso, add_rows ()funciona como açúcar sintático: na verdade, pega uma lista de listas e adiciona dados iterativamente.
Isso conclui a criação do nosso hiper. Em seguida, precisamos publicá-lo no servidor.
Publicar um arquivo em um servidor Tableau
Para acessar o tableau-server, usaremos o utilitário tabcmd - este é um utilitário de console que permite que você se conecte ao servidor e execute funções administrativas - crie usuários, grupos, livros e muito mais.
Vamos executar o comando tabcmd por meio do subprocesso Python.Popen:
popen = subprocess.Popen(
f'/opt/tableau/tabcmd/bin/tabcmd publish "{fullpath_hyper}" -n "{filename}" -o -r "A1_test" '
'-s http://tableau.domain.com -u "username" -p "password" --no-certcheck',
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
error = str(popen.communicate()[1])
return f" . {error}"
Passamos o seguinte comando e chaves para tabcmd:
- publicar : carregue um arquivo para o servidor
- -n (--name) : qual nome de arquivo estará no servidor
- -o (--overwrite) : se houver um arquivo com este nome, então sobrescreve
- -r “A1_test” (--project): ( )
- -s (--server): tableau-
- -u -p:
- --no-certcheck: SSL-
Descobrimos como criar um novo hiper, mas o que fazer quando o hiper consiste em dez containers e um deles recebeu novos dados? Vamos atualizar o hiper.
Quando novos dados chegam ao contêiner, o sistema verifica se há algum hypers que usa esse contêiner. Se houver, a tarefa é atualizar o hiper.
Para entender quais dados de qual contêiner estão no hiper, o sistema também cria uma coluna container_id adicional ao criar o hiper. Com essa abordagem, a atualização se torna muito simples:
- Pegamos o arquivo do servidor
- Excluímos todas as linhas do hyper, onde container_id é igual ao container atualizado
- Insira novas linhas
- Faça upload do arquivo sobrescrito de volta para o servidor.
O processo de recuperação de um arquivo é ligeiramente diferente do processo de download. Em primeiro lugar, não pegaremos o arquivo .hyper do servidor, mas sim o arquivo .tdsx, que depois descompactaremos e abriremos o próprio .hyper.
Para pegar o arquivo, usamos tabcmd:
popen = subprocess.Popen(
f'/opt/tableau/tabcmd/bin/tabcmd get "datasources/{filename_tdsx}" '
f'-s http://tableau.domain.com -u "username" -p "password" '
f'--no-certcheck -f "{fullpath_tdsx}"',
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
error = str(popen.communicate()[1])
return f". {error}"
Aqui usamos o seguinte comando e chaves:
- get : obtém um arquivo do servidor. Se o arquivo test.hyper estiver no servidor, você precisará consultar o arquivo test.tdsx e todos eles estão no diretório da fonte de dados (não consegui pesquisar no Google por que esse recurso do placar, se você sabe, compartilhe nos comentários)
- -f (--filename) : caminho completo, incluindo nome e extensão do arquivo, onde salvar o arquivo
Após o download do arquivo, ele deve ser descompactado por meio de um arquivo zip:
with zipfile.ZipFile(fullpath_tdsx, "r") as zip_ref:
zip_ref.extractall(path)
Após a descompactação, o hiper estará no diretório ./Data/Extracts .
Agora que temos a versão atual do arquivo, podemos remover linhas desnecessárias dele:
table_name = TableName("Extract", "Extract")
with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
with Connection(hyper.endpoint, self.fullpath_hyper) as connection:
connection.execute_query(
f"DELETE FROM {table_name} WHERE "
f'{escape_name("container_id")}={container_id}'
).close()
Bem, inserir e publicar um arquivo já foi descrito acima.
Conclusão
Qual é o resultado final? Tendo feito o trabalho de implementação da geração de hiper-arquivos e sua entrega automática ao tableau-server, reduzimos significativamente a carga na equipe de BI, ficou mais fácil atualizar os dados no painel e, principalmente, mais rápido. O próprio conhecimento da hyper api não foi doloroso, a documentação é bem escrita e a própria integração da tecnologia em nosso sistema foi fácil.
Agradecemos sua atenção! Se você tiver dúvidas ou comentários, por favor, deixe-os nos comentários.
O artigo foi escrito em conjunto com Vasily Lavrov (VasilyFromOpenSpace) — -