Mpld3: Browser/HTML geben die Abbildungsgröße anstelle von Abbildungsgröße * dpi an

Erstellt am 6. Feb. 2014  ·  31Kommentare  ·  Quelle: mpld3/mpld3

Ich genieße das Paket wirklich, vielen Dank fürs Teilen.

Dies ist eine vorgeschlagene Verbesserung, aber sie ist nicht ganz pythonisch. Aber andererseits ist d3js auch nicht :)

Es ist großartig, dass die festgelegte Größe der Figur auf dem Bildschirm angezeigt wird, aber ich denke, es wäre "reaktionsschneller", eine Option zu haben, um die Leinwandgröße auf das Begrenzungs-Div festzulegen und diese Div-Größe dann durch die Browserbreite / was auch immer bestimmen zu lassen sonst will der Entwickler.

Lohnt es sich, in _js.py und _object.py herumzuspielen, um das zum Laufen zu bringen?

Hilfreichster Kommentar

@cliffckerr Ich kann dies definitiv zu draw_figure hinzufügen, vielleicht als Argument, das ein Object nimmt und alle in diesem Objekt angegebenen Attribute auf svg setzt, damit Sie passieren könnten in etwas wie {viewBox: ..., width: '90vw', height: 'auto'} .

Ich wollte Sie und die anderen in diesem Thread fragen, ob dies Ihrer Meinung nach irgendwo anders hinzugefügt werden sollte, um zB fig_to_html() zu unterstützen. Wenn ja, sollten wir uns vielleicht eine Lösung dafür überlegen. Andernfalls lassen Sie es mich wissen und ich kann die Änderungen an draw_figure() sofort vornehmen.

Alle 31 Kommentare

Ich bin mir nicht ganz sicher, was Sie fragen ... Wir erhalten die Figurengröße in Zoll aus der Matplotlib-Figur und auch die dpi aus der Matplotlib-Figur. Wie schlagen Sie vor, dass wir das ändern? Danke

Anstatt die in Python festgelegte absolute Zahlengröße zu verwenden, meine ich so etwas:

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

Wobei figsizeinfo eine CSS-Spezifikation der Größe ist und mpld3 das div füllt. (meiner Erfahrung nach funktioniert Flotjs so)

Hmm ... diese Frage könnte meine Unerfahrenheit mit Web-Zeug verraten, aber ich dachte, dass SVG (das mpld3 verwendet) vollständig pixelbasiert ist und das nicht einfach geändert werden kann. Verwendet Flotjs SVG oder eine andere Spezifikation?

SVG-Grafiken (Scalable Vector Format) sind vektorbasiert, nicht pixelbasiert.

Hier ist ein Beispiel für ein mit Vanilla Matplotlib generiertes SVG-Bild, das mit der Fenstergröße skaliert:

http://bleepshow.pithy.io/SVG_Example

(deutlich an deinem Beispiel gerifft)

d3js und alle Leinwandzeichnungsfiguren sind meines Wissens Vektor, also sollten sie dazu in der Lage sein.

Der Trick, stelle ich mir vor, ist zu setzen

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

eher, als

figwidth = figsize[0] * dpi;

Ja, ich weiß, dass SVG vektorbasiert ist, aber wenn ich mich nicht irre, werden die Größe der Leinwand sowie die Größe der Markierungen und ihre Position auf dem Bildschirm in Punkten gemessen. Es scheint, als wäre es extrem kompliziert, SVG-Code zu schreiben, der für eine beliebige Figur mit einer beliebigen Fenstergröße korrekt skaliert.

Nein, es ist ziemlich einfach.

Hier ist der Hack, den ich auf Ihren fig_to_d3-Code angewendet habe:

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

was kommt als:

http://bleepshow.pithy.io/mpld3_window_scale_test

Jetzt sind die Beschriftungen an der falschen Stelle, aber es geht wieder "nur" darum, ihre Position auf das div und nicht unbedingt auf die Figur zu beziehen. Aber das Rendern der Figur funktioniert gut, soweit ich das beurteilen kann.

(Beachten Sie, dass sich die Größe nicht ändert, es sei denn, Sie aktualisieren, aber mit ein wenig Kleber ist das auch einfach.)

Ich bin gerade dabei, eine darauf basierende Lösung für mich zu manipulieren, ich versuche gerne, sie zu etwas Anständigem für einen Pull-Request zu polieren.

Responsive Größe der mpld3-Abbildung wird wirklich nützlich sein. Wenn Sie dies mit einer Schaltfläche (in der Symbolleiste) hinzufügen, um die aktuelle Figur in einem neuen Fenster öffnen zu können, kann die Größe der gesamten Figur leicht geändert werden.

+1

@hadim Ich möchte es "pythonisch" halten, also so etwas wie

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

wäre ein guter Auslöser

Ich stimme zu !

Ich verstehe, dass es einfach ist, die Figur in einer anderen Größe zu machen. Aber insgesamt bin ich immer noch nicht davon überzeugt, dass es eine gute Idee ist. Sobald die Abbildungsgröße geändert wurde, müssen alle Linienbreiten, Markierungspositionen, Markierungsgrößen, Schriftgrößen usw. gehackt werden, was den Code ohne großen Nutzen erheblich komplizierter macht. Außerdem hängt die korrekte Platzierung der Legende, der Achsenbeschriftungen und anderer Eigenschaften vom Befehl plt.draw() ab, der Annahmen auf der Grundlage der internen Figurengröße und der dpi-Einstellungen trifft, und ich habe ehrlich gesagt keine Ahnung, wie das leicht geändert werden könnte - Nachträglich angepasst.

Die Philosophie dieses mpld3 besteht darin, einfache Ausgaben zu erzeugen, die so genau wie möglich widerspiegeln, was in den Matplotlib-Figuren enthalten ist. Wenn Sie die Größe der Ausgabefigur ändern möchten, wäre die bevorzugte Methode, die Matplotlib-Figurengröße anzupassen. Alles andere scheint schnell zu einem großen Problem bei der Implementierung, dem Testen und der Wartung zu werden.

Was ist mit einem Plugin?

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

Ein Plugin wäre wahrscheinlich eine gute Möglichkeit, dies zu tun. Aber noch einmal, wie gehen Sie mit der Anpassung von Schriftgröße, Linienstärken, Markierungsgrößen, Legendenpositionen und Achsentextpositionen um? Aufgrund all dieser Probleme hätte ein solches Plugin ohne eine umfangreiche Infrastruktur zur Unterstützung der Größenänderung meines Erachtens nur einen sehr begrenzten Nutzen.

@jakevdp Ich bin anderer Meinung: Das einzige, was in meinem 3-Sekunden-Hack oben gelitten hat, waren die Label-Standorte, und auch hier geht es darum, besseres Javascript zu schreiben. Das DOM ermöglicht eine absolute Positionierung und relative Position ohne viel Arbeit.

So wie der Code jetzt implementiert ist, ist alles fest in die Figur codiert. Breite/Höhe, Linienstärke und Schriftgröße können mit ein wenig Arbeit linear gegen die Div-Größe skaliert werden. Der schwierigste Teil wäre das Seitenverhältnis, und das kann gesperrt werden. Sobald das Seitenverhältnis gesperrt ist, ist die lineare Skalierung ganz einfach

@hadim das ist eine gute idee.

Ich kenne d3.js nicht, also habe ich keine Ahnung. Aber weil wir an einer SVG-Datei arbeiten, kann ich mir wirklich nicht vorstellen, dass wir es nicht einfach machen können ...

Einige Links, um darüber nachzudenken, wie man es am besten macht:

+1 für @dansteingart. Warum nicht alle SVG-internen Längen und Positionen in relativen Koordinaten festlegen und dann vor der Ausgabe eine viewBox verwenden, um die Größe der gesamten Figur zu ändern und so mit dem figsize-Matplotlib-Parameter übereinzustimmen (oder in einem anderen Fall zu reagieren).

@dansteingart - Ich stimme zu, dass Sie es mit genügend JS-Hacks zum Laufen bringen könnten. Aber egal, ich bin nicht dafür, dieses Paket grundlegend zu modifizieren, um diese Funktion aufzunehmen.

Wieso den? Nun, ein Teil ist, dass es gegen die oben erwähnte Philosophie des Pakets verstößt, dass Plotelemente, Figurengrößen usw. über die Matplotlib-Schnittstelle definiert werden sollten.

Zweitens ist dies kein "Must-Have"-Feature, und die Codebasis für ein solches Nicht-Must-Have-Feature erheblich komplizierter zu machen, wird dem Paket auf lange Sicht schaden. Hintergrundinformationen dazu, warum dies so sein könnte, finden Sie im Vortrag von @ellisonbg vom letzten Jahr bei SciPy.

Wenn Sie an einem Plugin arbeiten möchten, um diese Art von Verhalten hinzuzufügen, können Sie das gerne tun. Wenn es gut funktioniert, kann es sich sogar lohnen, es in das Modul plugins aufzunehmen. Aber ich bin entschieden gegen größere Änderungen am grundlegenden Figurenlayout für neue Features, die der Philosophie des Pakets zuwiderlaufen, egal wie cool sie auch sein mögen.

Ich bin gerade dabei, mich damit zu beschäftigen, und es klingt für mich so, als könnte eine Art Plugin ein interessantes Verhalten zur Größenänderung von Figuren erreichen. @dansteingart startete diesen Thread mit einer Idee für ein Feature und einer Frage, ob es sich lohnt, mit der Implementierung des Features auf eine bestimmte Art und Weise zu beginnen. Als Antwort auf die Frage würde ich vorschlagen, dass Sie mit einem Plugin beginnen und sehen, wie weit das gehen kann, ohne _objects.py oder _js.py zu ändern. Aber wenn Sie Lust dazu haben, möchte ich einen Schritt zurücktreten und darüber sprechen, welches Problem wir zu lösen versuchen:

Es ist großartig, dass die festgelegte Größe der Figur auf dem Bildschirm angezeigt wird, aber ich denke, es wäre "reaktionsschneller", eine Option zu haben, um die Leinwandgröße auf das Begrenzungs-Div festzulegen und diese Div-Größe dann durch die Browserbreite / was auch immer bestimmen zu lassen sonst will der Entwickler.

Ich hätte gerne eine Klärung des Anwendungsfalls, in dem dies auftauchen wird. In meinem normalen Arbeitsablauf verwende ich ipython mit einem Notebook-Server, der in der Cloud läuft, wobei mein Webbrowser maximiert ist. So könnte es für mich in Frage kommen, wenn ich von meinem Arbeitsdisplay auf mein Laptop-Display wechsle und weniger Bildschirmbreite habe. Aber ich bin mir sicher, dass es andere Einstellungen gibt, an die andere denken. Was sonst?

@jakevdp Fair genug, und ich stimme zu, dass auf der Python-Seite _überhaupt_ nichts geändert werden sollte. Ich denke jedoch, dass es auf der JS-Seite alles Freiwild ist, und wenn der Sinn des Experiments darin besteht, die besten Aspekte von matplotlib und d3js zu heiraten, denke ich, dass es sich lohnt, es weiterzuverfolgen.

So wie ich es sehe, haben Sie, sobald Sie fig_to_d3 aufgerufen haben, kein Python-Land mehr und machen die Figur reaktionsfähig = besseres JS schreiben.

Was die Nützlichkeit von responsiven Seiten betrifft, stimme ich Ihnen nur für die iPython-Ausgabe zu, aber Python ist mehr als ein iPython-Notebook, und ich denke, letztendlich wird die Verwendung von mpl3d zum Erstellen von größenresponsiven Webseiten nützlicher sein, als Sie hier andeuten.

Wenn ich ein Detail gemäß Matplotlib vergrößern wollte, sollte ich nach Ihrer Logik einfach xlim / ylim ändern.

Nochmals vielen Dank für das großartige Paket, ich werde sehen, was ich mit einem Plugin nicht tun kann, und mich bei Ihnen melden.

@aflaxmann

Ich hätte gerne eine Klärung des Anwendungsfalls, in dem dies auftauchen wird. In meinem normalen Arbeitsablauf verwende ich ipython mit einem Notebook-Server, der in der Cloud läuft, wobei mein Webbrowser maximiert ist. So könnte es für mich in Frage kommen, wenn ich von meinem Arbeitsdisplay auf mein Laptop-Display wechsle und weniger Bildschirmbreite habe. Aber ich bin mir sicher, dass es andere Einstellungen gibt, an die andere denken. Was sonst?

siehe http://pithy.io

Das habe ich prägnant geschrieben, damit ich/meine Gruppe/meine Klassen Skripte schreiben können, die sich dann in Web-Apps verwandeln. Wenn die Figur ans Fenster passt, macht das Leben viel schöner. MPL3d gibt mir ein viel besseres Werkzeug, um interaktive Plots zu erstellen (ich hackte herum), und wenn die interaktiven Plots größenabhängig sind, ist alles gewonnen.

Danke @dansteingart. Denken Sie daran, dass das JS, obwohl es vor dem Python-Benutzer verborgen ist, immer noch Code ist und weiterhin gewartet werden muss. Meine Erfahrung im Laufe der Jahre mit der Open-Source-Entwicklung ist, dass oft Leute vorbeikommen, die begeistert sind, neue Funktionen zu implementieren, und sobald diese Funktionen zusammengeführt sind, fällt ihre Wartung und Instandhaltung auf die Schultern der wenigen Entwickler, die in das Paket in investiert haben langfristig. Ich bin bereits extrem übermotiviert bis zu dem Punkt, an dem ich gezwungen bin, Fehlerberichte zu häufig verwendeten Paketen zu ignorieren, die ich erst vor ein paar Jahren erstellt habe. Ich möchte wirklich nicht, dass mpld3 an diesen Punkt kommt.

Vielen Dank für Ihr Verständnis und ich freue mich darauf zu sehen, was Sie in einem Plugin zusammenstellen können.

@jakevdp dieser Punkt ist gut getroffen, und ich denke, die Plugin-Architektur funktioniert zu diesem Zweck gut. Wenn es kaputt geht, bin ich schuld, nicht deine (es sei denn, du änderst fig_to_d3 :-p )

Die Überarbeitung ist gelandet, und dies könnte als Plugin _etwas_ einfacher zu implementieren sein, zumindest für spezielle Situationen. Ich habe es jedoch nicht mit Blick auf diese Art von dynamischer Situation geschrieben, daher kann es einige Fallstricke geben. Lassen Sie mich wissen, wenn Sie diesbezüglich Fortschritte machen.

Hallo, danke für das tolle Projekt. Gab es Fortschritte bei diesem Thema? Wurde das Plugin / Feature entwickelt? Insbesondere beziehe ich mich auf "Option zum Festlegen der Leinwandgröße auf das Begrenzungs-Div". Danke.

Ich denke, das ist eine würdige Funktion, aber als Referenz nach dem Plotten können Sie d3 verwenden, um die Figur automatisch zu skalieren (hier mit der Fensterhöhe).

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

Hier sekundiere ich @hadim und @carlinmack , um viewBox zu verwenden. Es kann eigentlich ganz einfach sein. Wenn Sie beispielsweise derzeit <svg width="1200" height="800"> haben, versuchen Sie es stattdessen <svg viewBox="0 0 1200 800"> . Das ist es. Das viewBox definiert das Koordinatensystem innerhalb des SVG, und die Parameter width und height werden optional und definieren die Außenmaße wie jedes andere Element. Sie könnten auch width=100% haben oder den Wert direkt über CSS festlegen (die Breite und Höhe als CSS haben Vorrang vor Attributen).

Ich habe das erfolgreich mit einer neueren Chrome-Version verwendet. Die browserübergreifende Abhängigkeit sollte für die Details untersucht werden, aber ich denke, das Endergebnis ist klar.

PS: Es gibt einen Hintergrundartikel unter https://css-tricks.com/scale-svg , aber ich finde es verwirrend, wenn die Methode, die ich oben skizziert habe, einfach funktioniert (zumindest in modernen Browsern) und konzeptionell einfach ist: viewBox = "0 0 ${width} ${height}" für den inneren Code, width=... und height=... als SVG-Attribut oder CSS-Parameter für die Neuskalierung, falls erforderlich.

Ja, genau das hat @carlinmack vorgeschlagen. Ich war verwirrt von "verwenden Sie d3, um die Figur automatisch zu skalieren", da es sich um reines HTML / CSS handelt, aber ok, das Manipulieren des DOM ist das, was d3 tut. Das "preserveAspectRatio", wie von @carlinmack angegeben, ist meiner Meinung nach der Standardwert (zumindest wird das im css-tricks.com-Link erklärt). Warum geben Sie height="70vh" an, wenn das Seitenverhältnis trotzdem erhalten bleibt? Ist das eine Art Obergrenze für sehr schmale und große Figuren?

Richtig, ich wollte nicht, dass Porträtfiguren höher als das Ansichtsfenster sind :)

Abgesehen davon, dass es die Figur größer als nötig macht, nicht geeignet, wenn eine enge Anpassung an den umgebenden Inhalt erforderlich ist. Die Verwendung max-height funktioniert stattdessen für mich.

So lade ich am Ende mpld3-Figuren in den Browser:

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

Danke für die Diskussion @carlinmack @perrette ! Zufälligerweise sind wir gerade auch darauf gestoßen, die Lösung von @dkong-idm war:

    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 , wäre es viel Arbeit, diese automatische Skalierungsfunktion zu mpld3 hinzuzufügen, entweder als optionales Argument zu draw_figure oder als eigenständige Funktion?

@cliffckerr Ich kann dies definitiv zu draw_figure hinzufügen, vielleicht als Argument, das ein Object nimmt und alle in diesem Objekt angegebenen Attribute auf svg setzt, damit Sie passieren könnten in etwas wie {viewBox: ..., width: '90vw', height: 'auto'} .

Ich wollte Sie und die anderen in diesem Thread fragen, ob dies Ihrer Meinung nach irgendwo anders hinzugefügt werden sollte, um zB fig_to_html() zu unterstützen. Wenn ja, sollten wir uns vielleicht eine Lösung dafür überlegen. Andernfalls lassen Sie es mich wissen und ich kann die Änderungen an draw_figure() sofort vornehmen.

@vladh Ja, es wäre auch sehr nützlich in fig_to_html() .

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

sheldonpark picture sheldonpark  ·  7Kommentare

edvakf picture edvakf  ·  29Kommentare

jakevdp picture jakevdp  ·  4Kommentare

arnaudrenaud picture arnaudrenaud  ·  4Kommentare

dmiller7115 picture dmiller7115  ·  5Kommentare