Communitygithub.com

Jaschenn/my-skills

Crie gráficos estáticos prontos para uso com plotnine, em chinês ou inglês, para slides, documentos, revisões mensais/semanais e artigos de periódicos. Seleciona o gráfico conforme o que o usuário deseja comparar (ranking, tendência, variação entre dois pontos, composição, contribuição para uma mudança, cumprimento de meta, decomposição de métricas, distribuição, relação, estimativas com intervalos, resumo de KPI), renderiza no tamanho exato de saída (slide/doc/coluna de periódico) como SVG, PDF e PNG, e verifica automaticamente o layout. Gráficos para revisão de negócios seguem convenções de relatórios (▲▼ em vez de vermelho/verde, cor apenas para anomalias, legendas básicas, comparações alinhadas ao período). Use quando o usuário pedir para desenhar, plotar, criar gráfico ou visualizar dados, fazer uma figura para apresentação, relatório, revisão de negócios ou artigo, ou corrigir um gráfico que parece 'cheio mas não diz nada'. 用于画图、做图表、汇报/周报月报/论文配图、改图. Não é para gráficos interativos (plotly/echarts) ou dashboards.

O que é my-skills?

Este skill permite criar gráficos estáticos profissionais e prontos para uso direto em apresentações, relatórios de negócios, documentos e artigos acadêmicos, utilizando a biblioteca plotnine em Python. Ele oferece uma ampla gama de funções especializadas, como `C.bar_rank`, `C.line_trend` e `B.bullet`, que geram automaticamente o tipo de gráfico mais adequado com base no tipo de comparação desejada (por exemplo, ranking, tendência, composição). O skill suporta saída nos formatos SVG, PDF e PNG, com dimensões otimizadas para slides, documentos e colunas de periódicos, e inclui verificações de qualidade automáticas (QA) para garantir que não haja sobreposição de texto, rótulos cortados ou problemas de proporção. Ele também segue convenções específicas para relatórios de negócios, usando indicadores neutros (▲▼) e realçando apenas anomalias, em vez de usar cores vermelho/verde para indicar aumento ou queda.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Jaschenn/my-skills/tree/HEAD/storytelling-viz

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

storytelling-viz: Gráficos prontos para usar

O objetivo é um gráfico que pode ser colocado diretamente em uma apresentação, relatório ou periódico, não um gráfico de barras qualquer. Ele deve atender a três requisitos:

  • Tipo de gráfico correto. O tipo de gráfico corresponde ao que está sendo comparado, o foco é claro, as anotações são diretas e não são necessárias legendas.
  • Funciona com dados reais. Rótulos longos, valores negativos, dados ausentes, muitas categorias e valores atípicos são exibidos corretamente.
  • Atende às especificações de saída. Tamanho, tamanho da fonte, espessura da linha, formato e incorporação de fontes estão de acordo com o padrão.

Este skill cuida apenas do gráfico. A seleção do tópico, análise e estrutura do relatório estão fora do escopo deste skill.

Fluxo de trabalho

  1. Determine o cenário. O cenário determina as convenções padrão de cores, notas de rodapé, números, etc. (consulte "Convenções dos dois cenários"):
    • Relatório de negócios: gráficos de indicadores de negócios para a equipe de negócios ou gerência, independentemente da frequência e ocasião (relatórios semanais, mensais, painéis diários, atribuição de anomalias, páginas de dados de negócios para gerência / conselho / reunião estratégica). O critério é "o conteúdo são indicadores de negócios? O leitor tomará decisões de negócios com base nisso?", não "é um relatório semanal?".
    • Geral / Publicação: documentação de análise ou pesquisa, resultados experimentais, páginas da web, figuras de artigos, etc.
    • Se não tiver certeza, trate como relatório de negócios (convenções mais rigorosas, apenas uma linha de nota de rodapé a mais). O cenário de relatório de negócios deve primeiro ler references/business.md, mesmo que o código de exemplo pareça suficiente.
  2. Selecione a função de acordo com "o que comparar". Consulte a tabela de seleção de funções abaixo; ambos os cenários usam a mesma tabela.
  3. Escolha a especificação de saída. Use spec= para especificar (slide, documento, periódico...).
  4. Gere o gráfico e verifique os resultados automáticos. Após a renderização, figure() / save() imprime QA … ok ou lista cada aviso; o .qa do valor de retorno é a mesma lista de problemas (lista vazia = aprovado). Se houver avisos, corrija. Não entregue.
  5. Abra o PNG e verifique com a lista de verificação. Use Read para abrir o PNG gerado e verifique item por item de acordo com a lista de verificação no final.

Início rápido

# /// script
# dependencies = ["plotnine>=0.15,<0.16", "pandas", "pillow", "pypdf"]
# ///
import sys; sys.path.insert(0, "<skill>/scripts")
from viz_common import figure, titled, set_lang, save
import charts as C          # Funções de gráfico gerais
import biz as B             # Funções de gráfico para relatórios de negócios (podem ser misturadas com C)

set_lang("en")                                   # ou "zh" (padrão)
c = C.bar_rank(df, "channel", "gmv", highlight="POS", top_n=8, fmt="num")
figure([[(titled(c, title="POS é o único canal que encolheu"), 1)]],
       spec="doc", name="fig1", outdir="out")
save(c, "fig2", spec="journal-1col", outdir="out")   # Periódico, figura única: sem título

# Relatório de negócios: mesma figure(), mas deve incluir nota de rodapé com escopo
k = B.kpi_cards([dict(label="GMV (MTD)", value=16.5e6, prev=15.9e6, target=57e6, progress=0.29)])
figure([[(k, 1)]], spec="doc", size=(6.5, 2), name="kpi", outdir="out",
       caption="Base: GMV de pedidos pagos (USD); MTD até 9 Out vs 1–9 Set.")

Selecionar função

Encontre a linha de acordo com o que está sendo comparado. C. = charts.py, B. = biz.py; as funções marcadas com ★ são projetadas para relatórios de negócios, mas podem ser usadas em qualquer cenário.

O que compararFunçãoPontos-chave
Ranking, composição em um ponto no tempo, valores positivos/negativosC.bar_rank(df, cat, value, highlight=, top_n=, fmt=)Ordenação automática; mais de top_n itens são agrupados como "Outros/Other"; valores escritos dentro ou fora da barra
Quem causou a mudança (escala + taxa de crescimento + contribuição por dimensão)★ B.contrib_table(df, dim, prev, cur, top_n=, growth_alert=)Barras de dados de escala, ▲▼ taxa de crescimento, variação, contribuição para a variação total; linha de total no topo
Tendência: 1–N linhas juntasC.line_trend(df, x, y, group=, highlight=, band=, ref=)Anotação direta no final da linha; rótulos de mês em duas camadas automáticos para mensal; quebra de linha para dados ausentes; band= para faixa de confiança
Tendência: vários grupos separadamenteC.small_multiples(df, x, y, facet, highlight=)Mesma escala y em todos os painéis; linhas de fundo cinza são outros grupos
Tendência: um indicador de negócios + comparação com o mesmo período★ B.trend(df, date, value, compare="month"/"year", events=, as_of=, period_end=)Linha de comparação do período alinhada automaticamente; anota eventos; linha tracejada para período incompleto + estimativa
Progresso: acumulado no período vs meta★ B.cumulative_vs_target(df, date, value, target, start, end, as_of=, prior=)Real acumulado, linha de progresso da meta, acumulado do período anterior, estimativa baseada na média diária
Real vs Meta (várias linhas)★ B.bullet(df, cat, actual, target, progress=)Barra real + linha vertical da meta + linha tracejada de "deveria ter completado conforme progresso"; linhas com progresso atrasado ficam vermelhas
Real vs Intervalo (intervalo de previsão, faixa normal)C.range_dot(df, cat, low, high, value, flag="below", bench=)Linhas fora do intervalo usam cor de destaque; bench= especifica linha de referência
Dois valores por grupo (antes/depois, planejado/real)C.dumbbell(df, cat, start, end, labels=, highlight=)Ponto vazio = início, ponto cheio = fim; ordenado pelo fim; cada linha com taxa de variação
Dois períodos (início e fim), escalas de grupo muito diferentesC.slope(df, cat, start, end, mode="change", groups=)Quando mode="change", todas as linhas partem de 0%; linhas com início 0 são removidas e um WARNING é emitido
Ponte de aumento/redução: período anterior → contribuições → período atualC.waterfall(df, cat, delta, start=, end_label=)Aumento/redução usam cor neutra padrão + sinal de positivo/negativo; quando há significado de negócios para bom/ruim, passe up=PAL.good, down=PAL.bad
Decomposição de métricas (GMV = usuários × frequência × ticket médio)★ B.driver_tree(nodes)Cada nó mostra valor atual, ▲▼ variação, contribuição (log share para multiplicação, variação para adição)
Composição: proporção de cada parte em vários gruposC.stacked_100(df, cat, part, value, order=, focus_cat=)Partes em ordem natural, gradiente de uma cor; legenda em chart.legend
Tabela bidimensional (grupo × tempo, grupo × grupo)C.heatmap(df, x, y, value, fmt=)Branco para uma cor, escala de cores com percentis 2%–98%; texto branco em células escuras
Escala + Taxa (número de solicitações e taxa de aprovação)★ B.bar_line_dual(df, x, bar, line, line_range=)Único eixo duplo permitido: barra de escala (esquerda) + linha de taxa (direita), eixo da taxa começando em 0 ou faixa fixa
DistribuiçãoC.distribution(df, value, group=, kind="box"/"strip"/"hist")Ordenado pela mediana; bigodes em 1.5×IQR; amostragem quando muitos pontos
Relação entre duas variáveis contínuasC.scatter(df, x, y, label=, fit="linear", groups=, x_title=, y_title=)OLS + faixa de confiança, mostra R²; apenas pontos destacados são rotulados; groups= com legenda em chart.legend
Estimativas com intervalo de confiançaC.point_range(df, cat, est, low, high, ref=0)Gráfico de floresta; à direita escreve "estimativa [limite inferior, limite superior]"
Resumo de alguns números-chave (topo do relatório)★ B.kpi_cards([dict(label, value, prev, target, progress, spark, …), …])Números grandes, ▲▼ variação, taxa de atingimento vs progresso temporal, linha de tendência em miniatura; fica vermelho apenas se anômalo

Detalhes dos parâmetros das funções ★ (campos de kpi_cards, estrutura de nós de driver_tree, events= / as_of= de trend, prior= de cumulative_vs_target, regras de anomalia, alinhamento de comparação do período) estão em references/business.md.

Como escolher quando duas linhas se encaixam:

  • B.contrib_table vs C.waterfall: para ver simultaneamente escala, taxa de crescimento e proporção, ou mais de cerca de 8 dimensões → tabela; para explicar "como o período anterior se transformou no período atual passo a passo", com no máximo cerca de 8 itens → gráfico de cascata. Em páginas de atribuição, os dois geralmente ficam lado a lado.
  • C.line_trend vs C.small_multiples: se houver uma linha a ser destacada, ou no máximo 4 linhas → line_trend (mais de 4 linhas, adicione highlight=); para examinar a forma de cada grupo, com grupos de status semelhante → small_multiples. Em periódico de coluna única (3,5 pol), não cabem mais de 5 facetas; use line_trend + highlight=.
  • Dois valores por grupo, primeiro observe a escala de cada grupo: se forem semelhantes (máx / mín < cerca de 10 vezes) → C.dumbbell. Quando a diferença for de uma ordem de grandeza ou mais, os grupos pequenos ficam indistinguíveis na escala comum; então, desenhe diretamente a taxa de variação: no máximo cerca de 8 grupos → C.slope(mode="change"); mais grupos → primeiro calcule a coluna da taxa de variação e use C.bar_rank(df, cat, "chg", fmt="pct"), escrevendo a escala absoluta no rótulo ou em uma tabela ao lado.

Parâmetros e valores de retorno comuns a todas as funções:

  • highlight=: nome de categoria, lista ou função. Os itens selecionados usam a cor de destaque, o restante fica cinza, e o nome da categoria também fica em negrito na mesma cor.
  • fmt=: "num", "pct", "pp", "int", "raw", string de formato ou função; nd= controla o número de casas decimais.
  • Retorna um objeto de painel, que pode ser colocado diretamente em figure() / save(); chart + camada plotnine pode ser sobreposto; chart.legend é uma legenda de palavra colorida em uma linha, e titled() a usará automaticamente.

Quando não houver função adequada, desenhe manualmente:

  1. Use C.Frame(w, h, xr, yr, left=…, …) para reservar espaços em branco ao redor com base na largura real do texto (text_width); use C.axis_range(lo, hi) para o intervalo dos eixos.
  2. Use C.Canvas() com rect, seg, pt, text, line, band para desenhar elementos e cv.plot(fr) para gerar o gráfico.
  3. Encapsule como C.Chart(build), onde build(w, h) calcula o layout somente após o tamanho final ser determinado.

Não codifique coordenadas ou intervalos de eixos diretamente no código.

Convenções dos dois cenários

As funções em si desenham da mesma forma em ambos os cenários; as diferenças estão nessas convenções padrão. Não há parâmetro de "cenário" no código: as funções ★ já usam a aparência de relatório de negócios (▲▼, vermelho apenas para anomalias) e fazem figure() emitir um WARNING se faltar a nota de rodapé com escopo; os gráficos C. na mesma página devem receber parâmetros conforme a tabela abaixo (por exemplo, caption=, highlight=, cores up= / down=).

ItemGeral / PublicaçãoRelatório de negócios
Aumento/reduçãoUse good / bad somente quando houver significado claro de bom/ruim, e sempre com sinal de positivo/negativo ou textoSempre ▲▼ + cor neutra, não use vermelho/verde para aumento/redução
Quando usar vermelhoApenas para indicar "ruim"Apenas quando for julgado como anômalo (atrasado em relação ao progresso / acima do limite / fora de ±2σ histórico); anomalia em direção boa apenas em negrito
Chamar atenção do leitor "olhe aqui" (ex.: canal que puxa o crescimento para baixo)highlight= (cor de destaque laranja)Mesmo à esquerda; laranja significa apenas "olhe aqui", não indica bom/ruim; se é anômalo ou não é expresso por ▲▼ e vermelho
Nota de rodapé com escopoRecomendadoObrigatório (caption=): numerador, denominador, moeda, janela de tempo, base de comparação; se faltar, figure() emite WARNING
Base de comparaçãoConforme necessário para a análisePadrão: comparação mensal, acumulado do mês atual vs mesmo período do mês anterior (mesmo número de dias), cumprimento de meta; métricas de tráfego diário são alinhadas por dia da semana
Período atual incompletoCor clara ou linha tracejada + "Até o dia X"Mesmo à esquerda, e desenha a estimativa do período inteiro com base no progresso
NúmerosDe acordo com os hábitos da áreaValores em K/M/B; variação de taxa em pp, taxa de crescimento em %; unidade escrita no título ou nota
Eixo duploNão usarPermitido apenas "barra de escala + linha de taxa" (B.bar_line_dual)
Gráfico de pizza / roscaEvitar ao máximoProibido
Especificações comunsdoc, web, journal-*Apresentação PPT: slide; documentos Lark / Notion ou chat: doc (ilustração PNG); se o usuário não disser onde colocar, use doc

Razões e contraexemplos em references/rules.md (parte de relatório de negócios são B1–B7).

Especificações de saída (spec=)

specTamanho (polegadas)Tamanho da fonteFormato padrãoUso
slide / slide-4x313,33×7,5 / 10×7,512 / 11,5SVG com contornos, SVG editável, PNG (150dpi)Slides de apresentação
web8×4,510,5SVG, PNG (200dpi)Páginas web, e-mail
doc6,5×49PDF, SVG, PNG (300dpi)Uma figura no corpo do documento
a4 / letterPágina inteira10PDF, SVG, PNGRelatório de página inteira
journal-1col / -1.5col / -2col3,5 / 5,35 / 7,2 de largura7–7,5PDF, SVG, PNG (600dpi)Periódicos (89 / 136 / 183 mm)
  • size=(w, h) pode substituir o tamanho padrão, por exemplo, figura mais alta para periódico, linha de cartões KPI mais baixa.
  • O tamanho da fonte, espessura da linha e tamanho dos pontos são dimensionados de acordo com a especificação; PDFs usam fontes incorporadas Tipo 42, atendendo aos requisitos de periódicos.
  • Figuras de periódico não têm título; a legenda é escrita no texto: figura única use save(chart, name, spec="journal-1col"); figura com vários painéis use figure(rows, spec="journal-2col"), sem passar title=, numere os painéis com titled(chart, title="a").
  • figure() gera uma página inteira (pode ter título, subtítulo, nota de rodapé com escopo, vários painéis); save(chart, "fig2", spec="journal-1col") exporta apenas uma figura, sem título de página e nota de rodapé. Relatórios de negócios precisam de nota de rodapé, então use figure().
  • Dois tipos de SVG: <name>.svg tem o texto convertido em contornos, fica igual em qualquer lugar; <name>_editable.svg mantém o texto, pode ser editado no Figma / PPT, mas o computador de destino precisa ter a mesma fonte. No PPT, use o segundo se precisar editar texto, caso contrário, use o primeiro ou PNG.
  • slide / web não geram PDF por padrão; passe formats=("pdf", "png") quando necessário.

Combinar vários painéis

  • figure(rows, title=, subtitle=, caption=, spec=): rows=[[(panel, span), …], …], span é a largura relativa dentro da mesma linha.
  • A altura da linha é distribuída automaticamente por padrão: linhas que contêm apenas texto ou tabelas têm altura conforme o conteúdo, as linhas restantes dividem o espaço restante igualmente.
  • titled(chart, title=, legend=, note=): adiciona título do painel, linha de legenda e nota acima do gráfico; se legend= não for fornecido, usa chart.legend automaticamente.
  • O título suporta marcação rich text: **negrito**; [frase](focus) indica negrito e colorido. As cores podem ser focus, brand, good, bad, muted, ink ou #hex.
  • Outros painéis: text_panel(para(...)) para parágrafos, table_panel(rows, header=) para tabelas pequenas (colunas numéricas alinhadas automaticamente à direita), column(a, b) empilha dois painéis verticalmente.
  • Cada painel é renderizado separadamente no tamanho final, portanto, o tamanho da fonte é o valor real em pontos; ao concatenar SVGs, os IDs dos elementos de cada painel recebem automaticamente prefixos para evitar conflitos.

Regras visuais (as funções já aplicam por padrão; ao desenhar manualmente, siga-as)

  • Camadas de cor: focus (laranja) destaca a única coisa a ser observada, não representa bom/ruim; brand (azul escuro) para séries regulares; cinza para fundo e referência; o uso de good / bad segue o cenário (consulte a tabela acima).
  • Mesma cor = mesmo significado. Palavras-chave e dados correspondentes usam a mesma cor; elementos com significados diferentes não compartilham a mesma cor.
  • O texto deve ser legível. O contraste do texto deve ser de pelo menos 4,5:1. Ao usar cor de destaque para texto, use readable(color) para escurecer automaticamente; texto em fundo escuro use ink_on(fill). As funções tratam automaticamente; ao desenhar manualmente, Canvas.text também verificará e emitirá avisos.
  • Use anotações diretas em vez de legendas. O título do eixo não pode ser um nome de campo. Se todas as barras tiverem valores anotados, não são necessários eixo y e grade.
  • A ordenação deve fazer sentido. Se houver ordem natural (estágio, nível), siga-a; caso contrário, ordene pelo valor; coloque o item de destaque na posição mais visível; não ordene alfabeticamente.
  • Eixos. Gráficos de barras devem começar em 0; gráficos de linha, quando os dados estão próximos de 0, começam em 0 (zero="auto"); painéis relacionados usam a mesma escala; eixo duplo apenas para "barra de escala + linha de taxa".
  • Precisão. Suficiente para distinguir. Todos os números são calculados a partir dos dados, formatados com pct / pp / num / fmt_value, nunca escritos manualmente.
  • Escolha o gráfico de acordo com o que está sendo comparado: não use barras empilhadas de valor absoluto para mudanças de proporção; não desenhe vários gráficos de pizza para composição de vários grupos; quando houver mais de 4 linhas, use highlight= para destacar ou mude para pequenos múltiplos; se comparar dois itens em um gráfico de árvore ou radar levar mais de cerca de 2 segundos, mude para barras horizontais.
  • Problemas com dados devem ser marcados no gráfico. Período atual incompleto use cor clara ou linha tracejada e indique "Até o dia X"; amostras pequenas escreva n; dados ausentes quebrem a linha, não conecte.

Detalhes e razões em references/rules.md; exemplos de redesign do SWD em references/swd_practices.md.

Idioma e tipografia

  • set_lang("zh" | "en") altera simultaneamente a fonte padrão, rótulos de mês (chinês "1月\n2026", inglês "Jan\n2026") e "Outros/Other". Unidades monetárias em ambos os idiomas usam K/M/B por padrão; para usar 万/亿, use set_number_style("cn"). Rótulos que contêm chinês usam automaticamente fonte CJK. T("其他", "Other") obtém a string conforme o idioma atual.
  • Strings em chinês passam automaticamente por zh(): pontuação é convertida para largura total, espaços extras ao lado de pontuação de largura total são removidos, URLs e abreviações permanecem inalterados.
  • Regras de quebra de linha: números e palavras em inglês não são separados; pontuação não aparece no início da linha; parêntese de abertura não fica no final da linha.
  • Na inicialização, a fonte usada é exibida. Se aparecer WARNING: no CJK font, é necessário instalar o Noto Sans SC.

Verificação antes da entrega

Verificação automática (veja a saída de figure() ou .qa):

  • QA … ok: texto sem sobreposição, não saindo do painel, não cortado pela área de plotagem.
  • Sem avisos de contraste, nem avisos como rows do not fit, labels take most of the panel, cells too small, basis caption.

Verificação manual (abra o PNG):

  • Cobrindo o título e olhando apenas para o gráfico, consegue-se dizer o que ele expressa? Há apenas um foco?
  • Os números no título e nas anotações vêm da mesma variável que os valores no gráfico?
  • O tipo de gráfico corresponde ao que está sendo comparado (consulte a tabela de seleção de funções)? A ordenação faz sentido? Painéis relacionados têm a mesma escala?
  • O uso de cores segue as convenções do cenário? Removendo as cores, bom/ruim / aumento/redução ainda podem ser lidos a partir de símbolos ou texto?
  • Não há legendas padrão, nomes de campos, efeitos 3D ou gradientes?
  • O tamanho e o tamanho da fonte estão de acordo com a especificação de destino (periódico ~7pt, slides ≥12pt)? Os PDFs necessários já foram exportados?

Relatório de negócios adicional:

  • A nota de rodapé com escopo está completa? Os números foram conferidos com outros relatórios (no mesmo relatório, o mesmo número é consistente em todas as páginas)?
  • Unidade, casas decimais e formato são uniformes em todo o gráfico?

Ambiente e testes

  • Dependências: plotnine>=0.15,<0.16 (verificado em 0.15.8 + matplotlib 3.11), pandas, pillow, e pypdf para exportar PDF. O código está em scripts/viz_common.py (ferramentas base, especificações, exportação, QA), scripts/charts.py (C.) e scripts/biz.py (B.).
  • Local de saída: Defina outdir para um diretório visível pelo usuário; não escreva dentro do diretório do skill.
  • Galeria de exemplos: uv run references/gallery.py [outdir] (cada função C., slide em chinês + periódico em inglês); uv run references/business_gallery.py [outdir] (cada função B., slide em chinês + documento em inglês). A saída padrão é out/ no diretório atual.
  • Testes de regressão: uv run references/tests/edge_cases.py usa 38 conjuntos de dados de borda, em duas especificações (total de 76 itens) para verificar o desempenho das funções e se o QA está conforme o esperado; após alterar funções de gráfico, deve ser executado novamente e todos os testes devem passar antes de publicar.
  • Quando plotnine não puder ser instalado no sandbox: Primeiro use o simulador matplotlib em PYTHONPATH=<skill>/scripts/plotnine_preview para visualizar o layout e executar QA (regras de largura de linha e corte são consistentes com plotnine). O QA ok do simulador não é critério de entrega: passe o script para o usuário executar com uv run em sua máquina e informe claramente que este gráfico ainda não foi renderizado e verificado com plotnine real.

Habilidades Relacionadas