Skip to content

Commit

Permalink
breadth-first type inference
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Mar 10, 2021
1 parent 9772c7e commit 47d5f98
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 21 deletions.
34 changes: 19 additions & 15 deletions src/scales.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,31 +76,35 @@ function inferScaleType(key, channels, {type, domain, range}) {
}
if (registry.get(key) === radius) return "sqrt";
for (const {type} of channels) if (type !== undefined) return type;
if ((domain || range || []).length > 2) return inferOrdinalType(key);
if ((domain || range || []).length > 2) return asOrdinalType(key);
if (domain !== undefined) {
type = inferScaleTypeFromValues(key, domain);
if (type !== undefined) return type;
}
channels = channels.filter(({value}) => value !== undefined);
if (!channels.length) return;
for (const {value} of channels) {
type = inferScaleTypeFromValues(key, value);
if (type !== undefined) return type;
if (isOrdinal(domain)) return asOrdinalType(key);
if (isTemporal(domain)) return "utc";
return "linear";
}
// If any channel is ordinal or temporal, it takes priority.
const values = channels.map(({value}) => value).filter(value => value !== undefined);
if (values.some(isOrdinal)) return asOrdinalType(key);
if (values.some(isTemporal)) return "utc";
return "linear";
}

function inferScaleTypeFromValues(key, values) {
function isOrdinal(values) {
for (const value of values) {
if (value == null) continue;
if (typeof value === "string") return inferOrdinalType(key);
if (typeof value === "boolean") return inferOrdinalType(key);
if (value instanceof Date) return "utc";
return "linear";
const type = typeof value;
return type === "string" || type === "boolean";
}
}

function isTemporal(values) {
for (const value of values) {
if (value == null) continue;
return value instanceof Date;
}
}

// Positional scales default to a point scale instead of an ordinal scale.
function inferOrdinalType(key) {
function asOrdinalType(key) {
return registry.get(key) === position ? "point" : "ordinal";
}
63 changes: 63 additions & 0 deletions test/output/ordinalBar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions test/plots/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export {default as carsParcoords} from "./cars-parcoords.js";
export {default as covidIhmeProjectedDeaths} from "./covid-ihme-projected-deaths.js";
export {default as d3Survey2015Comfort} from "./d3-survey-2015-comfort.js";
export {default as d3Survey2015Why} from "./d3-survey-2015-why.js";
export {default as diamondsCaratPriceDots} from "./diamonds-carat-price-dots.js";
export {default as diamondsCaratPrice} from "./diamonds-carat-price.js";
export {default as diamondsCaratPriceDots} from "./diamonds-carat-price-dots.js";
export {default as empty} from "./empty.js";
export {default as gistempAnomaly} from "./gistemp-anomaly.js";
export {default as gistempAnomalyMoving} from "./gistemp-anomaly-moving.js";
Expand All @@ -25,26 +25,27 @@ export {default as letterFrequencyBar} from "./letter-frequency-bar.js";
export {default as letterFrequencyColumn} from "./letter-frequency-column.js";
export {default as letterFrequencyDot} from "./letter-frequency-dot.js";
export {default as letterFrequencyLollipop} from "./letter-frequency-lollipop.js";
export {default as metroInequalityChange} from "./metro-inequality-change.js";
export {default as metroInequality} from "./metro-inequality.js";
export {default as metroUnemploymentIndex} from "./metro-unemployment-index.js";
export {default as metroInequalityChange} from "./metro-inequality-change.js";
export {default as metroUnemployment} from "./metro-unemployment.js";
export {default as metroUnemploymentHighlight} from "./metro-unemployment-highlight.js";
export {default as metroUnemploymentIndex} from "./metro-unemployment-index.js";
export {default as metroUnemploymentMoving} from "./metro-unemployment-moving.js";
export {default as metroUnemploymentNormalize} from "./metro-unemployment-normalize.js";
export {default as metroUnemploymentRidgeline} from "./metro-unemployment-ridgeline.js";
export {default as metroUnemployment} from "./metro-unemployment.js";
export {default as mobyDickFaceted} from "./moby-dick-faceted.js";
export {default as mobyDickLetterFrequency} from "./moby-dick-letter-frequency.js";
export {default as mobyDickLetterPosition} from "./moby-dick-letter-position.js";
export {default as morleyBoxplot} from "./morley-boxplot.js";
export {default as moviesProfitByGenre} from "./movies-profit-by-genre.js";
export {default as musicRevenue} from "./music-revenue.js";
export {default as ordinalBar} from "./ordinal-bar.js";
export {default as penguinCulmen} from "./penguin-culmen.js";
export {default as penguinCulmenArray} from "./penguin-culmen-array.js";
export {default as penguinMassSexSpecies} from "./penguin-mass-sex-species.js";
export {default as penguinMass} from "./penguin-mass.js";
export {default as penguinMassSex} from "./penguin-mass-sex.js";
export {default as penguinMassSexSpecies} from "./penguin-mass-sex-species.js";
export {default as penguinMassSpecies} from "./penguin-mass-species.js";
export {default as penguinMass} from "./penguin-mass.js";
export {default as penguinSexMassCulmenSpecies} from "./penguin-sex-mass-culmen-species.js";
export {default as penguinSpeciesIsland} from "./penguin-species-island.js";
export {default as policeDeaths} from "./police-deaths.js";
Expand Down
13 changes: 13 additions & 0 deletions test/plots/ordinal-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Plot from "@observablehq/plot";

export default async function() {
return Plot.plot({
y: {
grid: true
},
marks: [
Plot.barY("ABCDEF", {x: (d, i) => i}),
Plot.ruleY([0])
]
});
}

0 comments on commit 47d5f98

Please sign in to comment.