为什么我的 D3 图表中的线是反转的?

问题描述

我使用生成的数据用 d3 绘制了一个折线图。

您在下方看到的线代表平均 9 分钟。

该线在图表末尾反转,然后反转回到自身,然后停在图表中间。我的问题是该行不应该在最后自己折回,我不知道为什么会这样。

我可以看到来自 2021-5-13-02-00 的数据导致了反转,但是,来自 2021-5-13-02-00 的数据不存在于生成的 CSV 文件数据的末尾。

我需要找出我没有输入的这些数据的来源。

任何解决此问题的帮助都会很棒,谢谢!

图:图表方向倒转了几下

enter image description here

margin = {top: 0,bottom: 20,left: 40,right: 20}

function apply_attrs(selection,attrs_func,xScale,yScale) {
    const s = attrs_func(selection,yScale)
    return s
}

function path_9_attr_func(selection,yScale) {
    selection
    .attr("fill","none")
    .attr("stroke","#00ff00")
    .attr("d",linefunc(xScale,yScale,9))
    return selection
}


function max_in_obj(data,property_name) {
    const high = data.reduce((acc,cur,ind) => {
        if (cur[property_name] > acc) {
            acc = cur[property_name]
        }
        return acc
    },data[0][property_name])
    return high
}

function min_in_obj(data,property_name) {
    const low = data.reduce((acc,ind) => {
        if (cur[property_name] < acc) {
            acc = cur[property_name]
        }
        return acc
    },data[0][property_name])
    return low
}

const linefunc = (xScale,n) => {
    return d3.line()
        .x((d,i,data) => {
            if (isNaN(d.date)) {
                return 0
            }
            let retval = xScale(d.date)
            if (d.date.getMinutes() === 30) {
                console.log("Here is 30",d,i)
            }
            return retval
        })
        .y((d,data) => {
            let sliced;
            sliced = data.slice(i-(n-1),i+1);
            const val = (min_in_obj(sliced,"low") + max_in_obj(sliced,"high")) / 2
            return yScale(val)
        }).defined((d,data) => i >= n-1)
}



const f = async () => {
    let num = 0
    let res = await fetch("https://gist.githubusercontent.com/KiYugadgeter/a8527ff1d9aa12f68b9d48d6fc09496a/raw/4bd019b8c6d6e2be2ecfd7180a64a6356fc34f11/stack_overflow_why_invert.csv")
    let d = await res.text()
    let min_value = 999999999
    let max_value = 0
    data = parsed_data = d3.csvParse(d,(dt) => {
        const j = dt.date.split("-").map((i)  => {
            const p = parseInt(i)
            return p
        })
        const date = new Date(j[0],j[1]-1,j[2],j[3],j[4],0)
        //num++
        console.log(date,dt)
        high_value = parseFloat(dt.high)
        low_value = parseFloat(dt.low)
        open_value = parseFloat(dt.open)
        close_value = parseFloat(dt.close)
        if (low_value < min_value) {
            min_value = low_value
        }
        if (high_value > max_value) {
            max_value = high_value
        }
        return {
            open: open_value,close: close_value,high: high_value,low: low_value,date: date
        }
    })
    return {data: data,min: min_value,max: max_value}

}

f().then((d) => {
    let num = 0
    let recently_date = d.data[d.data.length-1].date
    let minvalue = 99999999999
    let maxvalue = 0
    recently_date.setMinutes(recently_date.getMinutes() - (recently_date.getMinutes() % 30))
    recently_date.setSeconds(0)
    recently_date.setMilliseconds(0)
    recently_date = new Date(recently_date.getTime() + 30 * 60000)
    const limit_date = (recently_date - (60 * 90 * 1000))
    let data = d.data.filter((d) => {
        num++
        if (d.low < minvalue) {
            minvalue = d.low
        }
        if (d.high > maxvalue) {
            maxvalue = d.high
        }
        return true
    })
    const svg = d3.select("svg")
    const xScale = d3.scaleTime().domain(
        [
            limit_date,recently_date
        ]
    ).nice().range([margin.left,600-margin.left-margin.right])
    const yScale = d3.scaleLinear().domain([(maxvalue - (maxvalue%1000) + 2000),(minvalue - (minvalue%1000) - 2000)]).range([0,410-margin.top-margin.bottom])
    const canvas = svg.append("g").attr("width",600).attr("height",410)
    const clip = svg.append("clipPath").attr("id","clip").append("rect").attr("width",600-margin.left-margin.right).attr("height",410-margin.bottom-margin.top).attr("x",margin.left).attr("y",0)
    const candles_layer = canvas // Data group
        .append("g")
        .attr("stroke-linecap","square")
        .attr("stroke","black")
        .attr("clip-path","url(#clip)")
    const path_9min = canvas
        .append("path")

    const path_9_selection = path_9min
        .attr("clip-path","url(#clip)")
        .datum(data)
    apply_attrs(path_9_selection,path_9_attr_func,yScale)


    const xaxis = d3.axisBottom(xScale).ticks().tickFormat(d3.timeFormat("%H:%M"))
    const group_x = canvas
        .append("g")
        .attr("transform","translate(0," + String(410-margin.top - margin.bottom) + ")") // X axis
        .call(xaxis).style("font-size","5")
    const yaxis = d3.axisLeft(yScale)
    const group_y = canvas
        .append("g")
        .attr("class","axis axis--y")
        .attr("transform","translate(" + String(margin.left) + ",0)") // Y axis
        .call(yaxis)
        .style("font-size","5")  

})
<!DOCTYPE html>
<html lang="ja">
    <head>
        <Meta charset="utf-8">
        <script src="https://d3js.org/d3.v6.min.js"></script>
        <Meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        <svg id="svg" viewBox="0 0 600 410">
        </svg>

        

    </body>
</html>

解决方法

我清楚地看到您的上一个时间戳可以追溯到过去 (02:28:00 -> 02:00:00)

808: {open: 28935.51,close: 29053.97,high: 29139.7,low: 28875.91,date: Thu May 13 2021 02:28:00}
809: {open: 28996.66,close: 28812.63,high: 29046.49,low: 28760.27,date: Thu May 13 2021 02:00:00}

该代码段演示了从数据中删除最后一项可以解决问题:

margin = {top: 0,bottom: 20,left: 40,right: 20}

function apply_attrs(selection,attrs_func,xScale,yScale) {
    const s = attrs_func(selection,yScale)
    return s
}

function path_9_attr_func(selection,yScale) {
    selection
    .attr("fill","none")
    .attr("stroke","#00ff00")
    .attr("d",d => linefunc(xScale,yScale,9,d))
    return selection
}


function max_in_obj(data,property_name) {
    const high = data.reduce((acc,cur,ind) => {
        if (cur[property_name] > acc) {
            acc = cur[property_name]
        }
        return acc
    },data[0][property_name])
    return high
}

function min_in_obj(data,property_name) {
    const low = data.reduce((acc,ind) => {
        if (cur[property_name] < acc) {
            acc = cur[property_name]
        }
        return acc
    },data[0][property_name])
    return low
}

const linefunc = (xScale,n,d) => {
    return d3.line()
        .x((d,i,data) => {
            if (isNaN(d.date)) {
                return 0
            }
            let retval = xScale(d.date)
            if (d.date.getMinutes() === 30) {
                console.log("Here is 30",d,i)
            }
            return retval
        })
        .y((d,data) => {
            let sliced;
            sliced = data.slice(i-(n-1),i+1);
            const val = (min_in_obj(sliced,"low") + max_in_obj(sliced,"high")) / 2
            return yScale(val)
        }).defined((d,data) => i >= n-1)(d.slice(0,-1));
}



const f = async () => {
    let num = 0
    let res = await fetch("https://gist.githubusercontent.com/KiYugadgeter/a8527ff1d9aa12f68b9d48d6fc09496a/raw/4bd019b8c6d6e2be2ecfd7180a64a6356fc34f11/stack_overflow_why_invert.csv")
    let d = await res.text()
    let min_value = 999999999
    let max_value = 0
    data = parsed_data = d3.csvParse(d,(dt) => {
        const j = dt.date.split("-").map((i)  => {
            const p = parseInt(i)
            return p
        })
        const date = new Date(j[0],j[1]-1,j[2],j[3],j[4],0)
        //num++
        console.log(date,dt)
        high_value = parseFloat(dt.high)
        low_value = parseFloat(dt.low)
        open_value = parseFloat(dt.open)
        close_value = parseFloat(dt.close)
        if (low_value < min_value) {
            min_value = low_value
        }
        if (high_value > max_value) {
            max_value = high_value
        }
        return {
            open: open_value,close: close_value,high: high_value,low: low_value,date: date
        }
    })
    return {data: data,min: min_value,max: max_value}

}

f().then((d) => {
    let num = 0
    let recently_date = d.data[d.data.length-1].date
    let minvalue = 99999999999
    let maxvalue = 0
    recently_date.setMinutes(recently_date.getMinutes() - (recently_date.getMinutes() % 30))
    recently_date.setSeconds(0)
    recently_date.setMilliseconds(0)
    recently_date = new Date(recently_date.getTime() + 30 * 60000)
    const limit_date = (recently_date - (60 * 90 * 1000))
    let data = d.data.filter((d) => {
        num++
        if (d.low < minvalue) {
            minvalue = d.low
        }
        if (d.high > maxvalue) {
            maxvalue = d.high
        }
        return true
    })
    const svg = d3.select("svg")
    const xScale = d3.scaleTime().domain(
        [
            limit_date,recently_date
        ]
    ).nice().range([margin.left,600-margin.left-margin.right])
    const yScale = d3.scaleLinear().domain([(maxvalue - (maxvalue%1000) + 2000),(minvalue - (minvalue%1000) - 2000)]).range([0,410-margin.top-margin.bottom])
    const canvas = svg.append("g").attr("width",600).attr("height",410)
    const clip = svg.append("clipPath").attr("id","clip").append("rect").attr("width",600-margin.left-margin.right).attr("height",410-margin.bottom-margin.top).attr("x",margin.left).attr("y",0)
    const candles_layer = canvas // Data group
        .append("g")
        .attr("stroke-linecap","square")
        .attr("stroke","black")
        .attr("clip-path","url(#clip)")
    const path_9min = canvas
        .append("path")

    const path_9_selection = path_9min
        .attr("clip-path","url(#clip)")
        .datum(data)
    apply_attrs(path_9_selection,path_9_attr_func,yScale)


    const xaxis = d3.axisBottom(xScale).ticks().tickFormat(d3.timeFormat("%H:%M"))
    const group_x = canvas
        .append("g")
        .attr("transform","translate(0," + String(410-margin.top - margin.bottom) + ")") // X axis
        .call(xaxis).style("font-size","5")
    const yaxis = d3.axisLeft(yScale)
    const group_y = canvas
        .append("g")
        .attr("class","axis axis--y")
        .attr("transform","translate(" + String(margin.left) + ",0)") // Y axis
        .call(yaxis)
        .style("font-size","5")  

})
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <script src="https://d3js.org/d3.v6.min.js"></script>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        <svg id="svg" viewBox="0 0 600 410">
        </svg>

        

    </body>
</html>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...