Mpld3: Browser/HTML especifica o tamanho da figura em vez de figsize * dpi

Criado em 6 fev. 2014  ·  31Comentários  ·  Fonte: mpld3/mpld3

Realmente gostando do pacote, muito obrigado por compartilhar.

Esta é uma melhoria sugerida, mas não é muito pythonic. Mas, novamente, nem o d3js :)

É ótimo que o tamanho definido da figura seja refletido na tela, mas acho que seria "mais responsivo" ter uma opção para definir o tamanho da tela para o div delimitador e, em seguida, permitir que o tamanho do div seja determinado pela largura do navegador/qualquer coisa mais o desenvolvedor quer.

Vale a pena mexer em _js.py e _object.py para fazer isso?

Comentários muito úteis

@cliffckerr Definitivamente posso adicionar isso a draw_figure , talvez como um argumento que receba um Object e defina todos os atributos especificados nesse objeto no svg , para que você possa passar em algo como {viewBox: ..., width: '90vw', height: 'auto'} .

Eu queria perguntar a você e aos outros neste tópico se você acha que isso deve ser adicionado em qualquer outro lugar, para apoiar, por exemplo fig_to_html() . Se sim, talvez devêssemos pensar em uma solução para isso. Caso contrário, avise-me e posso fazer as alterações em draw_figure() imediatamente.

Todos 31 comentários

Não tenho certeza do que você está perguntando... Obtemos o tamanho da figura em polegadas da figura do matplotlib, e também obtemos o dpi da figura do matplotlib. Como você está sugerindo que mudemos isso? Obrigado

Em vez de usar o tamanho absoluto da figura definido em Python, quero dizer algo assim:

<div id='parent' class='figsizeinfo'>
//mpld3 generated code
</div>

Onde figsizeinfo é uma especificação CSS de tamanho, e o mpld3 preenche o div. (na minha experiência é assim que o flotjs funciona)

Hmm... esta pergunta pode trair minha inexperiência com coisas da web, mas eu pensei que o SVG (que o mpld3 usa) é inteiramente baseado em pixel, e isso não pode ser facilmente alterado. O flotjs usa SVG ou alguma outra especificação?

Os gráficos SVG (Scalable Vector Format) são baseados em vetores, não em pixels.

Aqui está um exemplo de uma imagem SVG gerada com vanilla matplotlib que escala com o tamanho da janela:

http://bleepshow.pithy.io/SVG_Example

(claramente riffed no seu exemplo)

d3js, e todas as figuras de desenho em tela, são vetoriais até onde eu entendo, então eles devem ser capazes de fazer isso.

O truque, imagino, é definir

figwidth = $(“#parent”).width //(jquery based, but you get the point )

ao invés de

figwidth = figsize[0] * dpi;

Sim, eu sei que o SVG é baseado em vetores, mas a menos que eu esteja enganado, o tamanho da tela, assim como o tamanho dos marcadores e sua posição na tela são medidos em pontos. Parece que seria extremamente complicado escrever código SVG que fosse dimensionado corretamente para uma figura arbitrária com um tamanho de janela arbitrário.

Não, é bem fácil.

Aqui está o hack que apliquei ao seu código fig_to_d3:

print mpld3.fig_to_d3(fig).replace("\n","\r").replace("var figwidth = 8.0 * 80","var figwidth = $(window).width()*.9;").replace("var figheight = 6.0 * 80","var figheight = $(window).height()*.9;")

que surge como:

http://bleepshow.pithy.io/mpld3_window_scale_test

Agora, os rótulos estão no lugar errado, mas novamente é "apenas" uma questão de referenciar sua posição para o div e não absolutamente para a figura. Mas a renderização da figura funciona bem até onde posso dizer.

(observe que isso não é redimensionado a menos que você atualize, mas com um pouco de cola que também é direto)

Atualmente, estou montando uma solução baseada nisso para mim, estou feliz em tentar poli-la em algo decente para um pull-request.

O tamanho responsivo da figura mpld3 será realmente útil. Se você adicionar isso com um botão (na barra de ferramentas) para poder abrir a figura atual em uma nova janela e a figura inteira será facilmente redimensionável.

+1

@hadim eu gostaria de mantê-lo "pythonic", então algo como

mpld3.fig_to_d3(fig,size="adaptive")

seria um bom gatilho

Eu concordo !

Eu entendo que fazer a figura de um tamanho diferente é fácil. Mas no geral ainda não estou convencido de que seja uma boa ideia. Uma vez que o tamanho da figura é modificado, isso envolverá hackear todas as larguras de linha, localização de marcadores, tamanhos de marcadores, tamanhos de fonte, etc., o que adicionará muita complicação ao código sem muito benefício. Além disso, o posicionamento correto da legenda, rótulos de eixo e outras propriedades depende do comando plt.draw(), que faz suposições com base no tamanho interno da figura e nas configurações de dpi, e honestamente não tenho ideia de como isso poderia ser facilmente refeito -ajustado após o fato.

A filosofia deste mpld3 é produzir saídas simples que reflitam o mais próximo possível o que está nas figuras do matplotlib. Se você quiser alterar o tamanho da figura de saída, o método preferido seria ajustar o tamanho da figura do matplotlib. Qualquer outra coisa parece que rapidamente se tornará uma grande dor de cabeça para implementar, testar e manter.

Que tal um plug-in?

plugins.connect(fig, plugins.ResponsiveSize())

Um plugin provavelmente seria uma boa maneira de fazer isso. Mas, novamente, como você lida com a adaptação do tamanho da fonte, larguras de linha, tamanhos de marcadores, locais de legenda e locais de texto de eixos? Por causa de todos esses problemas, sem uma infraestrutura extensa para dar suporte ao redimensionamento, acho que esse plug-in teria um alcance muito limitado de utilidade.

@jakevdp Discordo: a única coisa que sofreu no meu hack de 3 segundos acima foram os locais dos rótulos e, novamente, é uma questão de escrever um javascript melhor. O DOM permite posicionamento absoluto e posição relativa sem muito trabalho.

A forma como o código é implementado agora, tudo está codificado na figura. Com um pouco de trabalho, larguras/alturas, espessura de linha e tamanho da fonte podem ser dimensionados linearmente em relação ao tamanho da div. A parte mais complicada seria a proporção, e isso pode ser bloqueado. Uma vez que a proporção está bloqueada, dimensionar linearmente tudo é bastante simples

@hadim que é uma boa ideia.

Eu não conheço d3.js então não tenho idéia. Mas porque estamos trabalhando em um arquivo svg, eu realmente não imagino que não possamos fazer isso facilmente ...

Alguns links para começar a pensar na melhor forma de fazer:

+1 para @dansteinart. Por que não definir todo o comprimento e posição internos do SVG em coordenadas relativas e, em seguida, antes da saída, use um viewBox para redimensionar a figura inteira e, assim, corresponder ao parâmetro figsize matplotlib (ou ser responsivo em outro caso).

@dansteingart - Eu concordo que com hacks suficientes de JS, você pode fazer isso funcionar. Mas, independentemente disso, não sou a favor de modificar fundamentalmente este pacote para incluir esse recurso.

Por quê? Bem, uma parte é que vai contra a filosofia do pacote acima mencionada, que é que elementos de plotagem, tamanhos de figura, etc. devem ser definidos através da interface matplotlib.

A segunda é que este não é um recurso "obrigatório", e tornar a base de código significativamente mais complicada para um recurso tão não obrigatório prejudicará o pacote a longo prazo. Para algumas informações sobre por que isso pode ser, veja a palestra de @ellisonbg do ano passado no SciPy.

Se você quiser trabalhar em um plugin para adicionar esse tipo de comportamento, fique à vontade. Se funcionar bem, pode até valer a pena incluí-lo no módulo plugins . Mas sou firmemente contra grandes modificações no layout básico da figura para novos recursos que vão contra a filosofia do pacote, não importa o quão legal eles possam ser.

Estou apenas começando a entender isso, e me parece que algum tipo de plug-in pode realizar algum tipo de comportamento de redimensionamento de figura responsivo interessante. @dansteingart iniciou este tópico com uma ideia para um recurso e uma pergunta sobre se valia a pena começar a implementar o recurso de uma determinada maneira. Em resposta à pergunta, sugiro que você comece com um plugin e veja até onde isso pode ir sem alterar _objects.py ou _js.py. Mas se você estiver disposto, gostaria de dar um passo atrás e falar sobre qual problema estamos tentando resolver:

É ótimo que o tamanho definido da figura seja refletido na tela, mas acho que seria "mais responsivo" ter uma opção para definir o tamanho da tela para o div delimitador e, em seguida, permitir que o tamanho do div seja determinado pela largura do navegador/qualquer coisa mais o desenvolvedor quer.

Eu gostaria de alguns esclarecimentos sobre o caso de uso em que isso vai surgir. Em meu fluxo de trabalho regular, uso o ipython com um servidor de notebook em execução na nuvem, com meu navegador da Web maximizado. Portanto, pode acontecer para mim se eu mudar da tela do meu trabalho para a tela do meu laptop e tiver menos largura de tela. Mas tenho certeza de que existem outras configurações em que outros estão pensando. O quê mais?

@jakevdp Justo o suficiente, e eu concordo que não deve haver nada alterado _de forma alguma_ no lado do python. No entanto, acho que é um jogo justo no lado do JS, e se o objetivo do experimento é casar os melhores aspectos do matplotlib e do d3js, acho que vale a pena prosseguir.

Do jeito que eu vejo, uma vez que você chama fig_to_d3, você está fora da terra do python e tornando a figura responsiva = escrevendo melhor JS.

Quanto à utilidade das páginas responsivas, apenas para a saída do iPython, concordo com você, mas o python é mais do que o iPython notebook e acho que, em última análise, usar o mpl3d para criar páginas da Web responsivas de tamanho será mais útil do que você sugere aqui.

Pela sua lógica, se eu quisesse ampliar um detalhe de acordo com o matplotlib, deveria apenas alterar xlim/ylim.

Mais uma vez obrigado pelo ótimo pacote, vou ver o que não posso fazer com um plugin e te retorno.

@aflaxman

Eu gostaria de alguns esclarecimentos sobre o caso de uso em que isso vai surgir. Em meu fluxo de trabalho regular, uso o ipython com um servidor de notebook em execução na nuvem, com meu navegador da Web maximizado. Portanto, pode acontecer para mim se eu mudar da tela do meu trabalho para a tela do meu laptop e tiver menos largura de tela. Mas tenho certeza de que existem outras configurações em que outros estão pensando. O quê mais?

veja http://pithy.io

O que eu escrevi para permitir que eu/meu grupo/minhas classes escrevam scripts que se transformam em aplicativos da web. Ter a figura ajustada à janela torna a vida muito mais agradável. O MPL3d me dá ferramentas muito melhores para fazer gráficos interativos (eu estava hackeando flot por aí), e se os gráficos interativos são sensíveis ao tamanho, tudo é ganho.

Obrigado @dansteinart. Lembre-se de que, embora o JS esteja oculto para o usuário do Python, ele ainda é código e ainda precisa ser mantido. Minha experiência ao longo dos anos com desenvolvimento de código aberto é que muitas vezes aparecem pessoas empolgadas com a implementação de novos recursos e, uma vez que esses recursos são mesclados, sua manutenção e manutenção recaem sobre os ombros dos poucos desenvolvedores que investiram no pacote no longo prazo. Já estou extremamente comprometido a ponto de ser forçado a ignorar relatórios de bugs em pacotes bem usados ​​que criei há apenas alguns anos. Eu realmente não quero que o mpld3 chegue a esse ponto.

Obrigado pela compreensão, e estou ansioso para ver o que você pode juntar em um plugin.

@jakevdp esse ponto é bem aceito, e acho que a arquitetura do plugin funciona bem para esse fim. Se quebrar, a culpa será minha, não sua (ou seja, a menos que você mude fig_to_d3 :-p )

A reescrita chegou, e isso pode ser _ligeiramente_ mais fácil de implementar como um plugin, pelo menos para situações especiais. Eu não o escrevi com esse tipo de situação dinâmica em mente, então pode haver algumas armadilhas. Deixe-me saber se você fizer algum progresso nisso.

Olá, obrigado pelo ótimo projeto. Houve algum progresso nesta questão? O plugin/recurso foi desenvolvido? Especificamente, estou me referindo à "opção para definir o tamanho da tela para o div delimitador". Obrigado.

Eu acho que esse é um recurso digno, mas para referência após a plotagem você pode usar d3 para fazer a escala automática da figura (aqui com a altura da janela)

fetch("d3/" + figure + ".json")
    .then((response) => response.json())
    .then((data) => mpld3.draw_figure(figure, data))
    .then(() => {
        d3.select("#" + figure)
            .selectAll(".mpld3-figure")
            .attr("width", "100%")
            .attr("height", "70vh")
            .attr("viewBox", "0 0 1200 800")
            .attr("preserveAspectRatio", "xMinYMin meet");
    });

Aqui eu indicarei @hadim e @carlinmack para usar viewBox . Pode ser muito simples na verdade. Por exemplo, se você atualmente tem <svg width="1200" height="800"> , tente em vez disso <svg viewBox="0 0 1200 800"> . É isso. O viewBox define o sistema de coordenadas dentro do svg, e os parâmetros width e height se tornam opcionais e definem as dimensões externas como qualquer outro elemento. Você também pode ter width=100% ou definir o valor via css diretamente (a largura e a altura como css têm precedência sobre os atributos).

Eu usei isso com sucesso com uma versão recente do chrome. A dependência entre navegadores deve ser investigada para os detalhes, mas acho que o resultado final é claro.

PS: há um artigo de fundo em https://css-tricks.com/scale-svg , mas acho confuso quando o método que descrevi acima funciona (pelo menos em navegadores modernos) e é conceitualmente simples: viewBox = "0 0 ${width} ${height}" para o código interno, width=... e height=... como atributo svg ou parâmetro css para redimensionamento, se necessário.

Pois é, foi exatamente isso que @carlinmack sugeriu. Fiquei confuso com "usar d3 para fazer o dimensionamento automático da figura", pois é HTML/CSS puro, mas ok, manipular o DOM é o que o d3 faz. O "preserveAspectRatio", conforme indicado por @carlinmack , é o valor padrão que acredito (pelo menos é o que é explicado no link css-tricks.com). Por que você indicaria height="70vh" se a proporção for preservada de qualquer maneira? Isso é algum tipo de limite superior para figuras muito estreitas e altas?

correto, eu não queria que as figuras de retrato fossem maiores que a janela de visualização :)

Exceto que torna a figura maior do que o necessário, não é apropriado quando é necessário um ajuste apertado com o conteúdo ao redor. Usar max-height funciona para mim.

Aqui está como eu acabo carregando figuras mpld3 no navegador:

      let figureID = "id_12345" ; 
      let dataURL = figureID + ".json" ; 

      d3.json(dataURL).then( function(data) {
        mpld3.draw_figure(figureID, data);
        return data;
      }).then( function(data) {
        d3.select("#" + figureID)
          .selectAll(".mpld3-figure")
          .attr("width", null)   // remove "width" and "height" attributes as set by mpld3
          .attr("height", null)  
          .attr("viewBox", `0 0 ${data.width} ${data.height}`)
      });

Obrigado pela discussão @carlinmack @perrette ! Coincidentemente, acabamos de encontrar isso também, a solução do @dkong-idm foi:

    const resizeChart = (viewBoxSettings) => {
        let svg = document.getElementsByClassName("mpld3-figure");

        if (svg && svg.length > 0) {
            svg[0].setAttribute("viewBox",viewBoxSettings );
            svg[0].setAttribute("width", "90vw");
            svg[0].setAttribute("height", "auto");
        }
    }

@vladh , seria muito trabalhoso adicionar essa funcionalidade de dimensionamento automático a mpld3 , como um argumento opcional para draw_figure ou como uma função autônoma?

@cliffckerr Definitivamente posso adicionar isso a draw_figure , talvez como um argumento que receba um Object e defina todos os atributos especificados nesse objeto no svg , para que você possa passar em algo como {viewBox: ..., width: '90vw', height: 'auto'} .

Eu queria perguntar a você e aos outros neste tópico se você acha que isso deve ser adicionado em qualquer outro lugar, para apoiar, por exemplo fig_to_html() . Se sim, talvez devêssemos pensar em uma solução para isso. Caso contrário, avise-me e posso fazer as alterações em draw_figure() imediatamente.

@vladh Sim, também seria muito útil em fig_to_html() .

Esta página foi útil?
0 / 5 - 0 avaliações