2016-08-10 6 views
5

Ich möchte eine Skala erstellen, die einen Bereich aufeinanderfolgender Ganzzahlen (Indizes von Zeichen in einer Zeichenfolge) zu regelmäßigen Intervallen in einem anderen Bereich von ganzen Zahlen (Pixel, sagen 0-600). Das heißt, ich möchte den Pixeln Zeichen und umgekehrt so regelmäßig wie möglich zuweisen, wobei die Länge von Eins nicht notwendigerweise ein Vielfaches des anderen ist.Beste d3 Skalierung für Mapping Integer Bereich

Zum Beispiel Mapping [0,1,2,3] auf 400 Pixel, würde ich

0 -> 0-99 
1 -> 100-199 
2 -> 200-299 
3 -> 300-399 

erwarten und umgekehrt

0-99 -> 0 
100-199 -> 1 
200-299 -> 2 
300-399 -> 3 

während für die Zuordnung von 0-4000 auf 400 Pixel, Ich würde erwarten

0-9 -> 0 
10-19 -> 1 
etc. 

Was ist die beste Skala für diese in d3 verwenden?

Auf der einen Seite habe ich Angst, dass diskrete Skalen nicht die Tatsache verwenden, dass die Domäne gleichmäßig getrennt ist und eine große switch-Anweisung generiert, wenn die Anzahl der Elemente groß ist. Da ich die Skala für jedes Element verwenden werde, um ein Bild zu zeichnen, mache ich mir Sorgen um die Leistung.

Auf der anderen Seite wird eine lineare Skala wie

d3.scaleLinear() 
    .domain([0,399]) // 400 pixels 
    .rangeRound([0,3]) // 4 elements 

mir

0 0 
66 0 // 1st interval has 66 pixels 
67 1 
199 1 // other intervals have 132 pixels 
200 2 
332 2 
333 3 // last interval has 66 pixels 
400 3 

(fiddle)

so der Interpolator gibt ungleiche Intervalle (kürzer an dem Enden) gibt.

Edit: nicht d3 verwenden, ist es nicht schwer zu implementieren:

function coordinateFromSeqIndex(index, seqlength, canvasSize) { 
    return Math.floor(index * (canvasSize/seqlength)); 
} 
function seqIndexFromCoordinate(px, seqlength, canvasSize) { 
    return Math.floor((seqlength/canvasSize) * px); 
} 

Schade nur, wenn es nicht mit d3 Waage kommt, da es viel besser lesbar werden würde.

+0

Ich bin ein wenig unklar, was Ihre Eingabe und ausgegeben. In dem Teil "while for mapping 0-4000 to 400 pixels" haben Sie sich von der Abbildung eines Intervalls auf eine ganze Zahl entfernt, im Gegensatz zur Abbildung einer Ganzzahl auf ein Intervall wie im ersten Beispiel. –

+0

Ich meinte das, wenn Ich muss 4000 Objekte auf 400 Boxen verteilen, ich erwarte 10 davon in jeder Box, oder stell dir vor, du willst Zeichen in einer 4000 Zeichen langen Zeichenkette mit verschiedenen Farben darstellen und sie in eine Leinwand von 400x400 px zeichnen die Eingabe als Ganzzahlen, Sie müssen nur die Ausgabe der Skala runden, das ist kein Problem. – JulienD

Antwort

3

Die d3 Quantize Scale ist die beste Option, wenn Sie ein Intervall zuordnen möchten. Die Skala bildet jedoch zwischen diskreten Werten und einem kontinuierlichen Intervall ab. Ich weiß nicht genau, was Sie tun wollen, aber schauen wir uns einmal an, wie ich einige der Dinge, die Sie mit der Quantisierungsskala erwähnen, tun könnte.

Das Zuordnen von Ganzzahlen zu Intervallen ist einfach, solange Sie wissen, dass d3 halboffene Intervalle [,] verwendet, um die kontinuierliche Domäne aufzubrechen.

var s1 = d3.scaleQuantize() 
      .domain([0,400]) 
      .range([0,1,2,3]); 

s1.invertExtent(0); // the array [0,100] represents the interval [0,100) 
s1.invertExtent(1); // [100,200) 
s1.invertExtent(2); // [200,300) 
s1.invertExtent(3); // [300,400) 

Sie auch die diskreten Werte aufzählen könnte:

var interval = s.invertExtent(0); 
d3.range(interval[0], interval[1]); // [0, 1, ... , 399] 

Dies sind schöne Werte, die Sie gegeben haben, aber, und da Sie ganze Zahlen zu Intervallen von ganzen Zahlen abbilden wollen, werden wir, wenn Runden von Zahlen müssen sind nicht teilbar. Wir können Math.round jedoch nur verwenden.

var s2 = d3.scaleQuantize() 
      .domain([0,250]) 
      .range([0,1,2,3]); 

s2.invertExtent(0); // [0, 62.5) 
s2.invertExtent(0).map(Math.round); // [0,63) ... have to still interpret as half open 

Es gibt keine Zuordnung aus dem Intervall selbst auf die ganze Zahl, aber die Skala bildet einen Punkt in einem Abstand von der Domäne in dem Bereich, um seinen Wert (welche kontinuierlich ist).

Ich vermute auch, Sie haben die Ausgabe von rangeRound von der linearen Skala mit etwas anderem geschaltet. I erhalten

var srr = d3.scaleLinear() 
      .domain([0,3]) // 4 elements 
      .rangeRound([0,399]); 

[0,1,2,3].map(srr); // [0, 133, 266, 399] 

und

var srr2 = d3.scaleLinear() 
      .domain([0,4]) // 4 intervals created with 5 endpoints 
      .rangeRound([0,400]); 
[0,1,2,3,4].map(srr2); // [0, 100, 200, 300, 400] 

Der Ausgang sieht wie eine Skala, um uns mit einem Balkendiagramm, mit 50% Polsterung (dann würde jede Position der Mittelpunkt eines Intervalls, die 132 Pixel). Ich gehe davon aus, dass die Ursache darin liegt, dass rangeRound Runden verwendet, um zu interpolieren, anstatt zu floodieren.

Sie könnten auch eine Funktion für Balkendiagramme verwenden, wenn Sie die Breite des Intervalls möchten.

var sb = d3.scaleBand().padding(0).domain([0,1,2,3]).rangeRound([0,400]); 
[0,1,2,3].map(sb); // [0, 100, 200, 300] 
sb.bandwidth(); // 100 

Nicht, dass dies den Code einfacher macht.

Sobald ich zu den Funktionen komme, die Sie implementieren, scheint es, als wären die Anforderungen viel einfacher. Es gibt wirklich keine Intervalle. Das Problem ist, dass es kein Eins-zu-eins-Mapping gibt. Die beste Lösung ist entweder, was Sie getan haben, oder nur zwei mit einem benutzerdefinierten Interpolator lineare Skalen zu verwenden (auf den Boden zu finden, anstatt Runden.

var interpolateFloor = function (a,b) { 
    return function (t) { 
    return Math.floor(a * (1 - t) + b * t); 
    }; 
} 

var canvasSize = 400; 
var sequenceLength = 4000; 

var coordinateFromSequenceIndex = d3.scaleLinear() 
      .domain([0, sequenceLength]) 
      .range([0, canvasSize]) 
      .interpolate(interpolateFloor); 

var seqIndexFromCoordinate = d3.scaleLinear() 
      .domain([0, canvasSize ]) 
      .range([0, sequenceLength]) 
      .interpolate(interpolateFloor); 
+0

Tatsächlich habe ich in meinem Beispiel Bereich und Domäne umgekehrt, ich meinte https://fiddle.jshell.net/6q1s488L/1/. Ich korrigiere die ursprüngliche Frage. Aber deine Antwort hat es gelöst, danke. – JulienD

+0

Was ist aber mit der Quantisierungsskala, wenn statt [0,1,2,3] im Beispiel ich einen Bereich [1,2,3, .., 70000] habe? Würden Sie wegen der Leistung eine lineare Skala mit einem benutzerdefinierten Interpolator verwenden? – JulienD

Verwandte Themen