Mpld3: El navegador/HTML especifica el tamaño de la figura en lugar de figsize * dpi

Creado en 6 feb. 2014  ·  31Comentarios  ·  Fuente: mpld3/mpld3

Realmente disfruto el paquete, muchas gracias por compartir.

Esta es una mejora sugerida, pero no es del todo pitónica. Pero, de nuevo, tampoco lo es d3js :)

Es genial que el tamaño establecido de la figura se refleje en la pantalla, pero creo que sería "más receptivo" tener una opción para establecer el tamaño del lienzo en el div delimitador, y luego dejar que el tamaño del div sea determinado por el ancho del navegador/lo que sea más el desarrollador quiere.

¿Merece la pena jugar en _js.py y _object.py para que esto funcione?

Comentario más útil

@cliffckerr Definitivamente puedo agregar esto a draw_figure , tal vez como un argumento que toma Object y establece todos los atributos especificados en ese objeto en svg , para que pueda pasar en algo como {viewBox: ..., width: '90vw', height: 'auto'} .

Quería preguntarte a ti y a los demás en este hilo si crees que esto debería agregarse en otro lugar, para admitir, por ejemplo fig_to_html() . Si es así, tal vez deberíamos pensar en una solución a eso. De lo contrario, házmelo saber y puedo hacer los cambios a draw_figure() de inmediato.

Todos 31 comentarios

No estoy muy seguro de lo que está preguntando... Obtenemos el tamaño de la figura en pulgadas de la figura de matplotlib, y también obtenemos los dpi de la figura de matplotlib. ¿Cómo sugieres que cambiemos esto? Gracias

En lugar de usar el tamaño absoluto de la figura establecido en Python, me refiero a algo como esto:

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

Donde figsizeinfo es una especificación CSS de tamaño, y mpld3 llena el div. (según mi experiencia, así es como funciona flotjs)

Hmm... esta pregunta podría traicionar mi inexperiencia con las cosas web, pero pensé que SVG (que usa mpld3) está completamente basado en píxeles, y eso no se puede cambiar fácilmente. ¿Flotjs usa SVG o alguna otra especificación?

Los gráficos SVG (Scalable Vector Format) están basados ​​en vectores, no en píxeles.

Aquí hay un ejemplo de una imagen SVG generada con vanilla matplotlib que se escala con el tamaño de la ventana:

http://bleepshow.pithy.io/SVG_Example

(claramente riffed en su ejemplo)

d3js, y todas las figuras de dibujo de lienzo, son vectores, según tengo entendido, por lo que deberían poder hacer esto.

El truco, me imagino, es establecer

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

en vez de

figwidth = figsize[0] * dpi;

Sí, sé que SVG está basado en vectores, pero a menos que me equivoque, el tamaño del lienzo, así como el tamaño de los marcadores y su posición en la pantalla se miden en puntos. Parece que sería extremadamente complicado escribir código SVG que escalara correctamente para una figura arbitraria con un tamaño de ventana arbitrario.

No, es bastante fácil.

Aquí hay un truco que apliqué a tu 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

Ahora, las etiquetas están en el lugar equivocado, pero nuevamente es "solo" una cuestión de referenciar su posición al div y no absolutamente a la figura. Pero la representación de la figura funciona bien por lo que puedo decir.

(Tenga en cuenta que esto no cambia de tamaño a menos que actualice, pero con un poco de pegamento que también es sencillo)

Actualmente estoy improvisando una solución basada en esto para mí, estoy feliz de intentar pulirla y convertirla en algo decente para una solicitud de extracción.

El tamaño sensible de la figura mpld3 será realmente útil. Si agrega esto con un botón (en la barra de herramientas) para poder abrir la figura actual en una nueva ventana, la figura completa será fácilmente redimensionable.

+1

@hadim Me gustaría mantenerlo "pitónico", así que algo como

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

seria un buen detonante

Estoy de acuerdo !

Entiendo que hacer la figura de otro tamaño es fácil. Pero en general todavía no estoy convencido de que sea una buena idea. Una vez que se modifica el tamaño de la figura, implicará piratear todos los anchos de línea, las ubicaciones de los marcadores, los tamaños de los marcadores, los tamaños de fuente, etc., lo que agregará muchas complicaciones al código sin mucho beneficio. Además, la ubicación correcta de la leyenda, las etiquetas de los ejes y otras propiedades depende del comando plt.draw(), que hace suposiciones basadas en el tamaño de la figura interna y la configuración de dpi, y sinceramente no tengo idea de cómo podría volverse a configurar fácilmente. -ajustado después del hecho.

La filosofía de este mpld3 es producir resultados simples que reflejen lo más fielmente posible lo que hay en las cifras de matplotlib. Si desea cambiar el tamaño de la figura de salida, el método preferido sería ajustar el tamaño de la figura matplotlib. Cualquier otra cosa parece que se convertirá rápidamente en un gran dolor de cabeza para implementar, probar y mantener.

¿Qué pasa con un complemento?

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

Un complemento probablemente sería una buena manera de hacerlo. Pero, de nuevo, ¿cómo se adapta el tamaño de la fuente, el ancho de línea, el tamaño de los marcadores, las ubicaciones de las leyendas y las ubicaciones del texto de los ejes? Debido a todos esos problemas, sin una infraestructura extensa para admitir el cambio de tamaño, creo que dicho complemento tendría un rango de utilidad muy limitado.

@jakevdp No estoy de acuerdo: lo único que sufrió en mi truco de 3 segundos anterior fueron las ubicaciones de las etiquetas y, nuevamente, se trata de escribir mejor JavaScript. El DOM permite el posicionamiento absoluto y la posición relativa sin mucho trabajo.

La forma en que se implementa el código ahora, todo está codificado en la figura. Con un poco de trabajo, los anchos/altos, el grosor de línea y el tamaño de fuente se pueden escalar linealmente contra el tamaño div. La parte más complicada sería la relación de aspecto, y eso se puede bloquear. Una vez que la relación de aspecto está bloqueada, escalar todo linealmente es bastante sencillo

@hadim esa es una buena idea.

No sé d3.js, así que no tengo idea. Pero debido a que estamos trabajando en un archivo svg, realmente no imagino que no podamos hacerlo fácilmente...

Algunos enlaces para empezar a pensar en la mejor manera de hacerlo:

+1 para @dansteinart. ¿Por qué no configurar toda la longitud y posición interna de SVG en coordenadas relativas y luego, antes de la salida, usar un viewBox para cambiar el tamaño de toda la figura y así coincidir con el parámetro figsize matplotlib (o responder en otro caso).

@dansteingart : estoy de acuerdo en que con suficientes hacks de JS, podrías hacerlo funcionar. Pero independientemente, no estoy a favor de modificar fundamentalmente este paquete para incluir esa característica.

¿Por qué? Bueno, una pieza es que va en contra de la filosofía del paquete antes mencionada, que es que los elementos de la trama, los tamaños de las figuras, etc. deben definirse a través de la interfaz matplotlib.

En segundo lugar, esta no es una característica "imprescindible", y hacer que la base del código sea significativamente más complicada para una característica no imprescindible dañará el paquete a largo plazo. Para conocer algunos antecedentes sobre por qué podría ser esto, consulte la charla de @ellisonbg del año pasado en SciPy.

Si desea trabajar en un complemento para agregar este tipo de comportamiento, puede hacerlo. Si funciona bien, incluso puede valer la pena incluirlo en el módulo plugins . Pero estoy firmemente en contra de las modificaciones importantes en el diseño básico de la figura por nuevas características que van en contra de la filosofía del paquete, sin importar cuán geniales puedan ser.

Me estoy poniendo al día con esto, y me parece que algún tipo de complemento puede lograr algún tipo de comportamiento de cambio de tamaño de figura sensible interesante. @dansteinart comenzó este hilo con una idea para una función y una pregunta sobre si valía la pena comenzar a implementar la función de cierta manera. En respuesta a la pregunta, le sugiero que comience con un complemento y vea hasta dónde puede llegar sin cambiar _objects.py o _js.py. Pero si está dispuesto, me gustaría dar un paso atrás y hablar sobre el problema que estamos tratando de resolver:

Es genial que el tamaño establecido de la figura se refleje en la pantalla, pero creo que sería "más receptivo" tener una opción para establecer el tamaño del lienzo en el div delimitador, y luego dejar que el tamaño del div sea determinado por el ancho del navegador/lo que sea más el desarrollador quiere.

Me gustaría alguna aclaración sobre el caso de uso en el que esto va a surgir. En mi flujo de trabajo regular, uso ipython con un servidor portátil que se ejecuta en la nube, con mi navegador web maximizado. Por lo tanto, podría surgir para mí si cambio de la pantalla de mi trabajo a la pantalla de mi computadora portátil y tengo menos ancho de pantalla. Pero estoy seguro de que hay otras configuraciones en las que otros están pensando. ¿Qué más?

@jakevdp Está bien , y estoy de acuerdo en que no debería haber nada cambiado _en absoluto_ en el lado de python. Sin embargo, creo que todo es juego justo del lado de JS, y si el objetivo del experimento es casar los mejores aspectos de matplotlib y d3js, creo que vale la pena intentarlo.

A mi modo de ver, una vez que llamas a fig_to_d3, estás fuera de la tierra de python y haces que la figura responda = escribir mejor JS.

En cuanto a la utilidad de las páginas receptivas, solo para la salida de iPython, estoy de acuerdo con usted, pero python es más que un cuaderno iPython y creo que, en última instancia, usar mpl3d para crear páginas web receptivas de tamaño será más útil de lo que insinúa aquí.

Según su lógica, si quisiera ampliar un detalle de acuerdo con matplotlib, debería cambiar xlim/ylim.

Nuevamente, gracias por el excelente paquete, veré qué no puedo hacer con un complemento y me pondré en contacto con usted.

@aflaxman

Me gustaría alguna aclaración sobre el caso de uso en el que esto va a surgir. En mi flujo de trabajo regular, uso ipython con un servidor portátil que se ejecuta en la nube, con mi navegador web maximizado. Por lo tanto, podría surgir para mí si cambio de la pantalla de mi trabajo a la pantalla de mi computadora portátil y tengo menos ancho de pantalla. Pero estoy seguro de que hay otras configuraciones en las que otros están pensando. ¿Qué más?

ver http://pithy.io

Escribí concisamente para permitirme a mí, a mi grupo o a mis clases escribir guiones que luego se conviertan en aplicaciones web. Que la figura encaje en la ventana hace la vida mucho más agradable. MPL3d me brinda herramientas mucho mejores para hacer tramas interactivas (estaba pirateando), y si las tramas interactivas responden al tamaño, todo vale.

Gracias @dansteinart. Tenga en cuenta que, aunque el JS está oculto para el usuario de Python, sigue siendo un código y aún debe mantenerse. Mi experiencia a lo largo de los años con el desarrollo de código abierto es que a menudo aparecen personas que están entusiasmadas con la implementación de nuevas funciones, y una vez que esas funciones se fusionan, su mantenimiento recae sobre los hombros de los pocos desarrolladores que han invertido en el paquete en el a largo plazo. Ya estoy demasiado comprometido hasta el punto en que me veo obligado a ignorar los informes de errores en los paquetes bien utilizados que creé hace solo unos años. Realmente no quiero que mpld3 llegue a ese punto.

Gracias por su comprensión, y espero ver lo que puede juntar en un complemento.

@jakevdp ese punto está bien tomado, y creo que la arquitectura del complemento funciona bien para ese fin. Si se rompe, será culpa mía, no tuya (es decir, a menos que cambies fig_to_d3 :-p )

La reescritura ha aterrizado, y esto podría ser _ligeramente_ más fácil de implementar como complemento, al menos para situaciones especiales. Sin embargo, no lo escribí con este tipo de situación dinámica en mente, por lo que puede haber algunas trampas. Déjame saber si haces algún progreso en esto.

Hola, gracias por el gran proyecto. ¿Ha habido algún progreso en este tema? ¿Se ha desarrollado el complemento/función? Específicamente, me refiero a la "opción para establecer el tamaño del lienzo en el div delimitador". Gracias.

Creo que esta es una característica valiosa, pero como referencia después de trazar, puede usar d3 para hacer que la figura se escale automáticamente (aquí con la altura de la ventana)

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");
    });

Aquí apoyaré a @hadim y @carlinmack para que usen viewBox . Puede ser muy simple en realidad. Por ejemplo, si actualmente tiene <svg width="1200" height="800"> , pruebe <svg viewBox="0 0 1200 800"> . Eso es. El viewBox define el sistema de coordenadas dentro del svg, y los parámetros width y height se vuelven opcionales y definen las dimensiones externas como cualquier otro elemento. También podría tener width=100% o establecer el valor a través de css directamente (el ancho y la altura como css tienen prioridad sobre los atributos).

Lo usé con éxito con una versión reciente de Chrome. La dependencia entre navegadores debe investigarse en detalle, pero creo que el resultado final es claro.

PD: hay un artículo de antecedentes en https://css-tricks.com/scale-svg pero me resulta confuso cuando el método que describí anteriormente simplemente funciona (al menos en los navegadores modernos) y es conceptualmente simple: viewBox = "0 0 ${width} ${height}" para el código interno, width=... y height=... como atributo svg o parámetro css para el cambio de escala, si es necesario.

Sí, bueno, eso es exactamente lo que sugirió @carlinmack . Estaba confundido por "usar d3 para hacer que la figura se escale automáticamente", ya que es HTML / CSS puro, pero está bien, manipular el DOM es lo que hace d3. Creo que "preserveAspectRatio" como lo indica @carlinmack es el valor predeterminado (al menos eso es lo que se explica en el enlace css-tricks.com). ¿Por qué indicaría height="70vh" si la relación de aspecto se conserva de todos modos? ¿Es ese algún tipo de límite superior para figuras muy estrechas y altas?

correcto, no quería que las figuras de los retratos fueran más altas que la ventana gráfica :)

Excepto que hace que la figura sea más grande de lo necesario, no es apropiado cuando se necesita un ajuste perfecto con el contenido circundante. Usar max-height en su lugar funciona para mí.

Así es como termino cargando figuras mpld3 en el 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}`)
      });

¡Gracias por la discusión @carlinmack @perrette ! Coincidentemente, también nos encontramos con esto, la solución de @ dkong-idm fue:

    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 , ¿sería mucho trabajo agregar esta funcionalidad de escalado automático a mpld3 , ya sea como un argumento opcional a draw_figure o como una función independiente?

@cliffckerr Definitivamente puedo agregar esto a draw_figure , tal vez como un argumento que toma Object y establece todos los atributos especificados en ese objeto en svg , para que pueda pasar en algo como {viewBox: ..., width: '90vw', height: 'auto'} .

Quería preguntarte a ti y a los demás en este hilo si crees que esto debería agregarse en otro lugar, para admitir, por ejemplo fig_to_html() . Si es así, tal vez deberíamos pensar en una solución a eso. De lo contrario, házmelo saber y puedo hacer los cambios a draw_figure() de inmediato.

@vladh Sí, también sería muy útil en fig_to_html() .

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

sheldonpark picture sheldonpark  ·  7Comentarios

ajasja picture ajasja  ·  17Comentarios

pranet picture pranet  ·  6Comentarios

arnaudrenaud picture arnaudrenaud  ·  4Comentarios

dmiller7115 picture dmiller7115  ·  5Comentarios