Skip to content

Commit c130932

Browse files
authored
Merge pull request #1186 from plotly/fix-date-histograms
Fix date histograms
2 parents 846f2c7 + 2a57525 commit c130932

File tree

4 files changed

+166
-11
lines changed

4 files changed

+166
-11
lines changed

src/traces/histogram/calc.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ module.exports = function calc(gd, trace) {
4747
}
4848

4949
var binspec = trace[maindata + 'bins'],
50-
allbins = typeof binspec.size === 'string',
51-
bins = allbins ? [] : binspec,
50+
nonuniformBins = typeof binspec.size === 'string',
51+
bins = nonuniformBins ? [] : binspec,
5252
// make the empty bin array
5353
i2,
5454
binend,
@@ -85,13 +85,23 @@ module.exports = function calc(gd, trace) {
8585
size.push(sizeinit);
8686
// nonuniform bins (like months) we need to search,
8787
// rather than straight calculate the bin we're in
88-
if(allbins) bins.push(i);
88+
if(nonuniformBins) bins.push(i);
8989
// nonuniform bins also need nonuniform normalization factors
9090
if(densitynorm) inc.push(1 / (i2 - i));
9191
if(doavg) counts.push(0);
9292
i = i2;
9393
}
9494

95+
// for date axes we need bin bounds to be calcdata. For nonuniform bins
96+
// we already have this, but uniform with start/end/size they're still strings.
97+
if(!nonuniformBins && pa.type === 'date') {
98+
bins = {
99+
start: pa.r2c(bins.start),
100+
end: pa.r2c(bins.end),
101+
size: bins.size
102+
};
103+
}
104+
95105
var nMax = size.length;
96106
// bin the data
97107
for(i = 0; i < pos0.length; i++) {

src/traces/histogram2d/calc.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ module.exports = function calc(gd, trace) {
6464
z = [];
6565
var onecol = [],
6666
zerocol = [],
67-
xbins = (typeof(trace.xbins.size) === 'string') ? [] : trace.xbins,
68-
ybins = (typeof(trace.xbins.size) === 'string') ? [] : trace.ybins,
67+
nonuniformBinsX = (typeof(trace.xbins.size) === 'string'),
68+
nonuniformBinsY = (typeof(trace.ybins.size) === 'string'),
69+
xbins = nonuniformBinsX ? [] : trace.xbins,
70+
ybins = nonuniformBinsY ? [] : trace.ybins,
6971
total = 0,
7072
n,
7173
m,
@@ -103,10 +105,10 @@ module.exports = function calc(gd, trace) {
103105

104106
for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) {
105107
onecol.push(sizeinit);
106-
if(Array.isArray(xbins)) xbins.push(i);
108+
if(nonuniformBinsX) xbins.push(i);
107109
if(doavg) zerocol.push(0);
108110
}
109-
if(Array.isArray(xbins)) xbins.push(i);
111+
if(nonuniformBinsX) xbins.push(i);
110112

111113
var nx = onecol.length;
112114
x0 = trace.xbins.start;
@@ -121,10 +123,10 @@ module.exports = function calc(gd, trace) {
121123

122124
for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) {
123125
z.push(onecol.concat());
124-
if(Array.isArray(ybins)) ybins.push(i);
126+
if(nonuniformBinsY) ybins.push(i);
125127
if(doavg) counts.push(zerocol.concat());
126128
}
127-
if(Array.isArray(ybins)) ybins.push(i);
129+
if(nonuniformBinsY) ybins.push(i);
128130

129131
var ny = z.length;
130132
y0 = trace.ybins.start;
@@ -134,15 +136,32 @@ module.exports = function calc(gd, trace) {
134136

135137
if(densitynorm) {
136138
xinc = onecol.map(function(v, i) {
137-
if(Array.isArray(xbins)) return 1 / (xbins[i + 1] - xbins[i]);
139+
if(nonuniformBinsX) return 1 / (xbins[i + 1] - xbins[i]);
138140
return 1 / dx;
139141
});
140142
yinc = z.map(function(v, i) {
141-
if(Array.isArray(ybins)) return 1 / (ybins[i + 1] - ybins[i]);
143+
if(nonuniformBinsY) return 1 / (ybins[i + 1] - ybins[i]);
142144
return 1 / dy;
143145
});
144146
}
145147

148+
// for date axes we need bin bounds to be calcdata. For nonuniform bins
149+
// we already have this, but uniform with start/end/size they're still strings.
150+
if(!nonuniformBinsX && xa.type === 'date') {
151+
xbins = {
152+
start: xa.r2c(xbins.start),
153+
end: xa.r2c(xbins.end),
154+
size: xbins.size
155+
};
156+
}
157+
if(!nonuniformBinsY && ya.type === 'date') {
158+
ybins = {
159+
start: ya.r2c(ybins.start),
160+
end: ya.r2c(ybins.end),
161+
size: ybins.size
162+
};
163+
}
164+
146165

147166
// put data into bins
148167
for(i = 0; i < serieslen; i++) {

test/jasmine/tests/histogram2d_test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
var Plots = require('@src/plots/plots');
2+
var Lib = require('@src/lib');
3+
14
var supplyDefaults = require('@src/traces/histogram2d/defaults');
5+
var calc = require('@src/traces/histogram2d/calc');
26

37

48
describe('Test histogram2d', function() {
@@ -56,4 +60,53 @@ describe('Test histogram2d', function() {
5660

5761
});
5862

63+
64+
describe('calc', function() {
65+
function _calc(opts) {
66+
var base = { type: 'histogram2d' },
67+
trace = Lib.extendFlat({}, base, opts),
68+
gd = { data: [trace] };
69+
70+
Plots.supplyDefaults(gd);
71+
var fullTrace = gd._fullData[0];
72+
73+
var out = calc(gd, fullTrace);
74+
delete out.trace;
75+
return out;
76+
}
77+
78+
// remove tzJan/tzJuly when we move to UTC
79+
var oneDay = 24 * 3600000,
80+
tzJan = (new Date(1970, 0, 1)).getTimezoneOffset(),
81+
tzJuly = (new Date(1970, 6, 1)).getTimezoneOffset();
82+
83+
it('should handle both uniform and nonuniform date bins', function() {
84+
var out = _calc({
85+
x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'],
86+
nbinsx: 4,
87+
y: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'],
88+
nbinsy: 4
89+
});
90+
91+
expect(out.x0).toBe('1970-01-01');
92+
expect(out.dx).toBe(oneDay);
93+
94+
// TODO: even though the binning is done on non-uniform bins,
95+
// the display makes them linear (using only y0 and dy)
96+
// when we sort out https://github.com/plotly/plotly.js/issues/1151
97+
// lets also make it display the bins with nonuniform size,
98+
// and ensure we don't generate an extra bin on the end (see
99+
// first row of z below)
100+
expect(out.y0).toBe(tzJan === tzJuly ? '1969-07-02 14:24' : '1969-07-02 15:24');
101+
expect(out.dy).toBe(365.2 * oneDay);
102+
103+
expect(out.z).toEqual([
104+
[0, 0, 0, 0],
105+
[2, 0, 0, 0],
106+
[0, 1, 0, 0],
107+
[0, 0, 0, 0],
108+
[0, 0, 0, 1]
109+
]);
110+
});
111+
});
59112
});

test/jasmine/tests/histogram_test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
var Plots = require('@src/plots/plots');
2+
var Lib = require('@src/lib');
3+
14
var supplyDefaults = require('@src/traces/histogram/defaults');
5+
var calc = require('@src/traces/histogram/calc');
26

37

48
describe('Test histogram', function() {
@@ -127,4 +131,73 @@ describe('Test histogram', function() {
127131

128132
});
129133

134+
135+
describe('calc', function() {
136+
function _calc(opts) {
137+
var base = { type: 'histogram' },
138+
trace = Lib.extendFlat({}, base, opts),
139+
gd = { data: [trace] };
140+
141+
Plots.supplyDefaults(gd);
142+
var fullTrace = gd._fullData[0];
143+
144+
var out = calc(gd, fullTrace);
145+
delete out[0].trace;
146+
return out;
147+
}
148+
149+
// remove tzOffset when we move to UTC
150+
var tzOffset = (new Date(1970, 0, 1)).getTimezoneOffset() * 60000,
151+
oneDay = 24 * 3600000;
152+
153+
it('should handle auto dates with nonuniform (month) bins', function() {
154+
var out = _calc({
155+
x: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'],
156+
nbinsx: 4
157+
});
158+
159+
// TODO: https://github.com/plotly/plotly.js/issues/1151
160+
// these bins should shift when we implement that
161+
162+
// note that x1-x0 = 365 days, but the others are 365.5 days
163+
164+
// ALSO: this gives half-day gaps between all but the first two
165+
// bars. Now that we have explicit per-bar positioning, perhaps
166+
// we should fill the space, rather than insisting on equal-width
167+
// bars?
168+
var x0 = tzOffset + 15768000000,
169+
x1 = x0 + oneDay * 365,
170+
x2 = x1 + oneDay * 365.5,
171+
x3 = x2 + oneDay * 365.5;
172+
173+
expect(out).toEqual([
174+
// full calcdata has x and y too (and t in the first one),
175+
// but those come later from setPositions.
176+
{b: 0, p: x0, s: 2},
177+
{b: 0, p: x1, s: 1},
178+
{b: 0, p: x2, s: 0},
179+
{b: 0, p: x3, s: 1}
180+
]);
181+
});
182+
183+
it('should handle auto dates with uniform (day) bins', function() {
184+
var out = _calc({
185+
x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'],
186+
nbinsx: 4
187+
});
188+
189+
var x0 = tzOffset,
190+
x1 = x0 + oneDay,
191+
x2 = x1 + oneDay,
192+
x3 = x2 + oneDay;
193+
194+
expect(out).toEqual([
195+
{b: 0, p: x0, s: 2},
196+
{b: 0, p: x1, s: 1},
197+
{b: 0, p: x2, s: 0},
198+
{b: 0, p: x3, s: 1}
199+
]);
200+
});
201+
202+
});
130203
});

0 commit comments

Comments
 (0)