D3 displaying same period of time in xAxis - date

I'm trying to display 36 hours array
labels = ["01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00", "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00"]
with data
data = [16.1, 15.55, 14.12, 11.81, 9.637, 8.022, 6.625, 5.105, 5.216, 8.398, 11.4, 10.86, 10.52, 11.14, 15.37, 13.26, 11.33, 8.572, 12.21, 16.98, 12.43, 10.4, 10.09, 10.19, 9.361, 9.068, 9.763, 12.06, 15.52, 17.32, 15.53, 14.46, 14.05, 24.24, 12.26, 11.01]
but displayed only 24 elements from 1:00 to 00:00. How can I configure D3 axis for displaying data with repeated time stamps?

Don't just use the hours, create the axis with full timestamps and then format the axis labels to only show the time:
var width = 600;
var height = 50;
var yPad = 50;
var xPad = 50;
var xTicks = 18;
var now = d3.utcHour(new Date());
var h36 = d3.utcHour.offset(now, 36);
var svg = d3
.select("#d3")
.append('svg')
.attr('width', width)
.attr('height', height);
var xScale = d3
.scaleTime()
.domain([now, h36])
.rangeRound([1 * xPad, width - xPad]);
var xAxis = d3
.axisBottom(xScale)
.ticks(xTicks)
.tickFormat(d3.timeFormat('%H00'));
svg
.append('g')
.attr('class', 'xaxis')
.attr('transform', 'translate(0, ' + (height - yPad) + ')')
.call(xAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="d3"></div>

Related

How can i make fair value gap boxes and hide when filled automatically? calculation would be low[0] > high{-[2]

Fair value gap coding in pinescript?
I have tried to write code but unable to add more functions on it.
//#version=5
indicator('Fair Value Gap Akash Mehto', overlay=true)
boxLength = input.int(title='boxLength', defval=6, group='General', inline='General')
boxTransparency = input.int(title='boxTransparency', defval=85, group='General', inline='General')
bullishFvg = low[0] > high[2]
bearishFvg = high[0] < low[2]
if bullishFvg
box.new(left=bar_index - 2, top=low[0], right=bar_index + boxLength, bottom=high[2], bgcolor=color.new(color.rgb(28, 202, 121), boxTransparency), text="FVG", text_size = "tiny", text_halign = text.align_right, text_color = color.green, border_color=color.new(color.green, boxTransparency))
if bearishFvg
box.new(left=bar_index - 2, top=low[2], right=bar_index + boxLength, bottom=high[0], bgcolor=color.new(color.rgb(240, 46, 46), boxTransparency), text="FVG", text_size = "tiny", text_halign = text.align_right, text_color = color.red, border_color=color.new(color.green, boxTransparency))

Window/global variable in JavaScript returning a NaN value early?

NOTE: I have attached a link here to a (semi) working version of my program.
I am programming a stock charting program using the Finnhub and P5.js libraries.
I am encountering a problem debugging the code for generating the Y-axis values based on the total range of values of a stock. When calling a window/global variable (window.yAxisRange), the for loop the variable is called within appears to be running before anything is written to said variable.
Specifically, the console shows that the console.log() code that I wrote to check the value of the yAxisRange variable displays twice (once per each iteration of that particular loop). The first instance shows a returned NaN value. The graph within the browser views gives a good example of the problem, in that there appears to be viable numbers being generated, but a NaN value is overlayed on top of them for some reason.
I am perplexed as to why I am receiving a NaN value, as well as why the loop in which the window.yAxisRange is called is running so early.
My code is below (I apologize for any discrepancies as I have spent some time adjusting it):
import finnhub from 'https://cdn.skypack.dev/finnhub';
// Finance API Function Call Below
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "c5cam0iad3ib55bb0ajg"
const finnhubClient = new finnhub.DefaultApi()
let stockTicker = prompt("Please enter the symbol of the stock to chart:");
var yAxisAutoRange = 0
finnhubClient.stockCandles(
stockTicker, "D", ((Math.trunc(Date.now() / 1000)) - 8640000), Date.now(),
(error, data, response) => {
console.log("The maximum stock value of the timeframe is: " + max(Object.values(data.h)));
console.log("The minimum stock value of the timeframe is: " + min(Object.values(data.l)));
console.log("The values of the stock's highs are: " + (Object.values(data.h)))
console.log("The values of the stock's lows are: " + (Object.values(data.l)))
let yAxisRangeUpper = (max(Object.values(data.h))) + (max(Object.values(data.h)) - min(Object.values(data.l)))
let yAxisRangeLower = (min(Object.values(data.l))) - (max(Object.values(data.h)) - min(Object.values(data.l)))
let yAxisRange = (max(Object.values(data.h)) - min(Object.values(data.l))) / 2
window.yAxisRange = Math.trunc(yAxisRange)
console.log("yAxis Range: " + window.yAxisRange);
console.log("range maximum value : " + yAxisRangeUpper);
console.log("range minimum value : " + yAxisRangeLower);
window.yAxisAutoRange = Math.trunc(yAxisRangeUpper / 23);
console.log("yAxisAutoRange: " + yAxisRange);
let xAxisDayArray = []
let dayArrayLoop = data.t.length
let dayArrayLoop2 = data.t.length
window.xAxisDayArray = []
window.dayArrayLoop = data.t.length
window.dayArrayLoop2 = data.t.length
for (dayArrayLoop = dayArrayLoop; dayArrayLoop > dayArrayLoop2 - 44; dayArrayLoop = dayArrayLoop - 1) {
xAxisDayArray[dayArrayLoop] = JSON.stringify(new Date(data.t[dayArrayLoop - 1] * 1000));
xAxisDayArray[dayArrayLoop] = parseInt(xAxisDayArray[dayArrayLoop].slice(9, 11))
window.xAxisDayArray[dayArrayLoop] = xAxisDayArray[dayArrayLoop]
//console.log("Data point " + dayArrayLoop + "for date " + xAxisDayArray[dayArrayLoop])
}
}
);
var month = 0
// P5 Chart Background Below
globalThis.setup = function() {
createCanvas(1080, 768);
background(220);
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const d = new Date();
window.month = months[d.getMonth()];
console.log(month)
const day = new Date();
day.getDate();
textSize(18)
text(window.month.slice(0, 3), 1030, 755);
}
globalThis.draw = function() {
// rect(yAxisAutoRange, 300, 10, 100);
let i = 20;
for (i = 20; i < 1020; i = i + 25) {
let y = 0;
for (line(1025, 0, 1025, 768), y = 0; y < 730; y = y + 15) {
stroke(140, 25);
line(i, y, (i + 10), y)
};
}
let z = 10;
for (z = 10; z < 730; z = z + 15) {
let x = 25;
for (line(0, 730, 1080, 730), x = 25; x < 1026; x = x + 25) {
line(x, z, x, (z + 10))
}
}
let yAxisLine = 730
let yAxisIndLocate = 710
let yAxisMultiple = 1
let yAxisNum = String(parseInt(window.yAxisAutoRange * yAxisMultiple))
// May need to simplify loop structure so as to cancel recurring drawing functions
for (yAxisLine = 705, yAxisIndLocate = 710, yAxisMultiple = 1; yAxisLine > 14, yAxisIndLocate > 14, yAxisMultiple < 24; yAxisLine = yAxisLine - 30, yAxisIndLocate = yAxisIndLocate - 30, yAxisMultiple = yAxisMultiple + 1) {
console.log("Y Axis Auto Range = " + window.yAxisAutoRange);
stroke(51)
line(1025, yAxisLine, 1040, yAxisLine);
textSize(18);
//console.log("Endless Loop Test " + yAxisNum) //window.yAxisAutoRange not registerring
//console.log("Y Axis Ind = " + window.yAxisRange);
text(window.yAxisRange / 23 * yAxisMultiple, 1045, yAxisIndLocate);
}
let xAxisLine = 1000
let xAxisIndLocate = 0
for (xAxisLine = 1000, window.dayArrayLoop = window.dayArrayLoop; xAxisLine > 24, window.dayArrayLoop > window.dayArrayLoop2 - 41; xAxisLine = xAxisLine - 25, window.dayArrayLoop = window.dayArrayLoop - 1) {
stroke(51)
line(xAxisLine, 730, xAxisLine, 745);
//console.log(xAxisLine);
text(parseInt(window.xAxisDayArray[dayArrayLoop]), xAxisLine - 5, 760)
noLoop()
}
}
First off, here's how you get this code running as a snippet on stack overflow (with added debugging):
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script type="module">
import finnhub from 'https://cdn.skypack.dev/finnhub';
// Finance API Function Call Below
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "c5cam0iad3ib55bb0ajg"
const finnhubClient = new finnhub.DefaultApi()
let stockTicker = prompt("Please enter the symbol of the stock to chart:");
var yAxisAutoRange = 0;
console.log('module scope yAxisAutoRange: ' + yAxisAutoRange);
console.log('window.yAxisAutoRange: ' + window.yAxisAutoRange);
console.log("Requesting data for " + stockTicker);
finnhubClient.stockCandles(
stockTicker, "D", ((Math.trunc(Date.now() / 1000)) - 8640000), Date.now(),
(error, data, response) => {
console.log("The maximum stock value of the timeframe is: " + max(Object.values(data.h)));
console.log("The minimum stock value of the timeframe is: " + min(Object.values(data.l)));
console.log("The values of the stock's highs are: " + (Object.values(data.h)))
console.log("The values of the stock's lows are: " + (Object.values(data.l)))
let yAxisRangeUpper = (max(Object.values(data.h))) + (max(Object.values(data.h)) - min(Object.values(data.l)))
let yAxisRangeLower = (min(Object.values(data.l))) - (max(Object.values(data.h)) - min(Object.values(data.l)))
let yAxisRange = (max(Object.values(data.h)) - min(Object.values(data.l))) / 2
window.yAxisRange = Math.trunc(yAxisRange)
console.log("yAxis Range: " + window.yAxisRange);
console.log("range maximum value : " + yAxisRangeUpper);
console.log("range minimum value : " + yAxisRangeLower);
window.yAxisAutoRange = Math.trunc(yAxisRangeUpper / 23);
console.log("yAxisAutoRange: " + yAxisRange);
let xAxisDayArray = []
let dayArrayLoop = data.t.length
let dayArrayLoop2 = data.t.length
window.xAxisDayArray = []
window.dayArrayLoop = data.t.length
window.dayArrayLoop2 = data.t.length
for (dayArrayLoop = dayArrayLoop; dayArrayLoop > dayArrayLoop2 - 44; dayArrayLoop = dayArrayLoop - 1) {
xAxisDayArray[dayArrayLoop] = JSON.stringify(new Date(data.t[dayArrayLoop - 1] * 1000));
xAxisDayArray[dayArrayLoop] = parseInt(xAxisDayArray[dayArrayLoop].slice(9, 11))
window.xAxisDayArray[dayArrayLoop] = xAxisDayArray[dayArrayLoop]
//console.log("Data point " + dayArrayLoop + "for date " + xAxisDayArray[dayArrayLoop])
}
}
);
var month = 0
console.log("Declaring p5.js sketch");
// P5 Chart Background Below
globalThis.setup = function() {
console.log("p5.js sketch setup");
createCanvas(1080, 768);
background(220);
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const d = new Date();
window.month = months[d.getMonth()];
console.log(month)
const day = new Date();
day.getDate();
textSize(18)
text(window.month.slice(0, 3), 1030, 755);
// Slow down the framerate so we don't spam the logs during debugging
frameRate(5);
}
globalThis.draw = function() {
console.log('p5.js draw');
// rect(yAxisAutoRange, 300, 10, 100);
let i = 20;
for (i = 20; i < 1020; i = i + 25) {
let y = 0;
for (line(1025, 0, 1025, 768), y = 0; y < 730; y = y + 15) {
stroke(140, 25);
line(i, y, (i + 10), y)
};
}
let z = 10;
for (z = 10; z < 730; z = z + 15) {
let x = 25;
for (line(0, 730, 1080, 730), x = 25; x < 1026; x = x + 25) {
line(x, z, x, (z + 10))
}
}
let yAxisLine = 730
let yAxisIndLocate = 710
let yAxisMultiple = 1
let yAxisNum = String(parseInt(window.yAxisAutoRange * yAxisMultiple))
console.log("Y Axis Auto Range = " + window.yAxisAutoRange);
// May need to simplify loop structure so as to cancel recurring drawing functions
for (yAxisLine = 705, yAxisIndLocate = 710, yAxisMultiple = 1; yAxisLine > 14, yAxisIndLocate > 14, yAxisMultiple < 24; yAxisLine = yAxisLine - 30, yAxisIndLocate = yAxisIndLocate - 30, yAxisMultiple = yAxisMultiple + 1) {
stroke(51)
line(1025, yAxisLine, 1040, yAxisLine);
textSize(18);
//console.log("Endless Loop Test " + yAxisNum) //window.yAxisAutoRange not registerring
//console.log("Y Axis Ind = " + window.yAxisRange);
text(window.yAxisRange / 23 * yAxisMultiple, 1045, yAxisIndLocate);
}
let xAxisLine = 1000
let xAxisIndLocate = 0
for (xAxisLine = 1000, window.dayArrayLoop = window.dayArrayLoop; xAxisLine > 24, window.dayArrayLoop > window.dayArrayLoop2 - 41; xAxisLine = xAxisLine - 25, window.dayArrayLoop = window.dayArrayLoop - 1) {
stroke(51)
line(xAxisLine, 730, xAxisLine, 745);
//console.log(xAxisLine);
text(parseInt(window.xAxisDayArray[dayArrayLoop]), xAxisLine - 5, 760)
noLoop()
}
}
</script>
Because you are using a top level import, and Stack Snippets don't support that, you have to move all of your JavaScript into the HTML (which is kind of a bummer and StackOverflow should fix this).
That being said, the fundamental issue here is that you are making an asynchronous request for data, and then immediately initializing a p5.js sketch that depends on that data (or at least on a value calculated from it). Consider the following console log output:
Requesting data for tsla
Declaring p5.js sketch
p5.js sketch setup
You just changed the value of "month", which was a p5 function. This could cause problems later if you're not careful.
0
p5.js draw
Y Axis Auto Range = undefined
... repeatedly
p5.js draw
Y Axis Auto Range = undefined
The maximum stock value of the timeframe is: 1243.49
The minimum stock value of the timeframe is: 813.35
The values of the stock's highs are: 820.25,843.21,...
The values of the stock's lows are: 813.35,822.35,...
yAxis Range: 215
range maximum value : 1673.63
range minimum value : 383.21
yAxisAutoRange: 215.07
p5.js draw
Y Axis Auto Range = 72
Hopefully it is self evident from this that it is only natural that yAxisAutoRange is initially undefined and thus window.yAxisRange / 23 * yAxisMultiple is NaN: because the code that computes it hasn't run yet!
Also, be careful using the , operator in the condition expression for for loops! false, true is true and true, false is false. I'm not sure that is what you meant to do (perhaps you should be using either || for logical or, or && for logical and.

tm_compass does not appear inside of graph

I'm trying to make a map of Europe using tmap and the eurostat package.
I want to add a compass and a scale bar to the map. However they don't appear inside the graph, but outside of the map, at the bottom. Does anyone know what I'm doing wrong? I want the compass at the left top of the map, and the scale bar at the right bottom.
countries = gisco_get_countries(
year = "2016",
epsg = "3035",
resolution = "3"
)
br = c(0,40,50,65,80,150)
tm_shape(countries, bbox = c(23, 14, 74, 55) * 10e4) +
tm_fill("#E0E0E0") +
tm_shape(nuts2.sf) +
tm_fill(
"fatal_inj_30day",
breaks = br,
style = "fixed",
palette = "Blues",
alpha = .7,
title = "Fatalities per million inhabitants \n(2018-2019)"
) +
tm_compass(position = c("left","top")) +
tm_scale_bar(position = c("right","bottom")) +
tm_shape(countries) +
tm_borders(lwd = .25) +
tm_layout(
bg.color = "#F2F2F2",
outer.bg.color = "white",
legend.bg.color = "white",
legend.frame = "black",
legend.title.size = 0.8,
inner.margins = c(0, 0, 0, 0),
outer.margins = c(0, 0, 0, 0),
frame = TRUE,
frame.lwd = 0,
attr.outside = TRUE,
legend.position = c("right", "top"),
main.title = "Note: regions with 10 fatalities or less are not included in the Figure",
main.title.position = "left",
main.title.size = 0.7
)

Custom format for google chart vAxis label

I have an area chart and on the vAxis I'd like the labels to read # hrs. For the data below, the Total Time Spent column is an integer of total minutes. I'd like the vAxis label to show as # hrs. So for example, the first row would read 31.3 hrs. I'm not finding a way to create a custom formatter for that where I can convert the minutes into decimal hours and append the "hrs" suffix. Any ideas? Thanks.
var data = google.visualization.arrayToDataTable([
['Date', 'Deals Processed', 'Total Time Spent'],
[ '12/17/2020', 4, 1878],
[ '12/18/2020', 3, 290],
[ '12/21/2020', 8, 2772],
[ '12/22/2020', 6, 1084],
[ '12/23/2020', 4, 1175]
]);
we can customize the y-axis labels by providing our own ticks.
for each tick, we can use object notation, and provide the value (v:) and formatted value (f:)
{v: 1878, f: '31.3 hrs'}
we can also customize the tooltip when hovering the point by using the same approach,
in the data table.
['12/17/2020', 4, {v: 1878, f: '31.3 hrs'}],
or we can set the formatted value after the data table has been created.
data.setFormattedValue(0, 2, (1878 / 60).toFixed(1) + ' hrs');
to build the ticks, we can use data table method --> getColumnRange(columnIndex)
this will return an object with properties for min & max
which we can use to build our ticks array.
// build y-axis labels
var range = data.getColumnRange(2);
var max = Math.ceil(range.max / 100) * 100;
var ticks = [];
for (var i = 0; i <= max + 600; i = i + 600) {
ticks.push({
v: i,
f: (i / 60).toFixed(0) + ' hrs'
});
}
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['Date', 'Deals Processed', 'Total Time Spent'],
[ '12/17/2020', 4, 1878],
[ '12/18/2020', 3, 290],
[ '12/21/2020', 8, 2772],
[ '12/22/2020', 6, 1084],
[ '12/23/2020', 4, 1175]
]);
// format tooltip
for (var i = 0; i < data.getNumberOfRows(); i++) {
var minutes = data.getValue(i, 2);
data.setFormattedValue(i, 2, (minutes / 60).toFixed(1) + ' hrs');
}
// build y-axis labels
var range = data.getColumnRange(2);
var max = Math.ceil(range.max / 100) * 100;
var ticks = [];
for (var i = 0; i <= max + 600; i = i + 600) {
ticks.push({
v: i,
f: (i / 60).toFixed(0) + ' hrs'
});
}
var options = {
height: 400,
vAxis: {
ticks: ticks
}
};
var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Use words as tick labels with unequal intervals in D3.js

I am creating a D3.js plot of multiple years of data. I would like to use the month names as the the axis labels. I am currently using the number of days into the year corresponding with the first day of that month as the labels as shown in the screenshot of my x-axis:
I believe all the relevant code is shown below:
var margins = {'top' : 20, 'right' : 20, 'bottom' : 50, 'left' : 70};
var height = 500;
var width = 960;
var plotHeight = height - margins.top - margins.bottom;
var plotWidth = width - margins.right - margins.left;
var x = d3.scaleLinear()
.domain([0,366])
.range([margins.left, plotWidth]);
// I am not concerned with leap years
var monthDays = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
var monthAbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var svg = d3.select('.svg').attr('width',width).attr('height',height);
svg.append('g').attr('transform', 'translate(0,' + plotHeight + ')').call(d3.axisBottom(x).tickValues(monthDays));
Is there some way to map the position on the x-axis to the correct month (i.e.: 31 to Feb.)? I have tried using d3.timeMonth/d3.timeMonths and ordinal scales but haven't found something suitable yet.
You can use tickFormat:
.tickFormat(function(d,i){ return monthAbr[i]})
Here is a working demo based on your code:
var margins = {'top' : 20, 'right' : 20, 'bottom' : 50, 'left' : 20};
var height = 100;
var width = 600;
var plotHeight = height - margins.top - margins.bottom;
var plotWidth = width - margins.right - margins.left;
var x = d3.scaleLinear()
.domain([0,366])
.range([margins.left, plotWidth]);
// I am not concerned with leap years
var monthDays = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
var monthAbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var svg = d3.select('body').append("svg").attr('width',width).attr('height',height);
svg.append('g').attr('transform', 'translate(0,' + plotHeight + ')').call(d3.axisBottom(x).tickValues(monthDays).tickFormat(function(d,i){ return monthAbr[i]}));
<script src="https://d3js.org/d3.v4.min.js"></script>