我必须上传一个90 MB的CSV文件,然后用chart.js分析它作为一个图表。CSV文件有每分钟记录的测量值。90 MB的数据几乎是一年的数据。我已经将网站响应时间设置为一个很高的值。但是我的代码正在流失。这就是为什么我只能显示一定数量的数据值,然后点击通过每隔一段时间就看一次图表。即使这样也很慢,不太好。对于评估,至少每月一次的概述会更好。但我不知道我还能做什么调整。你有什么想法吗?
HTML
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSV Diagramm mit Chart.js</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="drop-area" class="drop-area" style="width: 100%;" ondrop="handleDrop(event)" ondragover="handleDragOver(event)">
<p>Datei hier ablegen</p>
<input type="file" id="csvFileInput" accept=".csv" style="display:none;" onchange="handleUpload()">
</div>
<div class="chart-container" style="width: 100%;">
<canvas id="myChart"></canvas>
</div>
<button onclick="showPreviousData()">Vorheriger Tag</button>
<button onclick="showNextData()">Nächster Tag</button>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/2.0.1/chartjs-plugin-zoom.min.js" integrity="sha512-wUYbRPLV5zs6IqvWd88HIqZU/b8TBx+I8LEioQ/UC0t5EMCLApqhIAnUg7EsAzdbhhdgW07TqYDdH3QEXRcPOQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="script.js"></script>
</body>
</html>
字符集
JS
let startIndex = 0;
const displayCount = 1440;
let labels = [];
let datasets = [];
let originalDatasetVisibility = [];
function handleUpload() {
const fileInput = document.getElementById('csvFileInput');
const file = fileInput.files[0];
handleFile(file);
}
function processData(csvData) {
const rows = csvData.split('\n');
labels = [];
datasets = [];
originalDatasetVisibility = [];
const colors = ['rgba(255, 0, 0, 1)', 'rgba(0, 255, 0, 1)', 'rgba(255, 255, 0, 1)', 'rgba(0, 0, 255, 1)'];
const columns = rows[0].split(';');
for (let i = 1; i < columns.length; i++) {
const data = [];
const currentLabel = columns[i];
const color = colors[i - 1];
for (let j = 1; j < rows.length; j++) {
const cols = rows[j].split(';');
if (i === 1) {
labels.push(cols[0]);
}
data.push(parseFloat(cols[i]));
}
const dataset = {
label: currentLabel,
data: data,
backgroundColor: color,
borderColor: color,
fill: false,
borderWidth: 1,
pointRadius: 1,
};
datasets.push(dataset);
originalDatasetVisibility.push(true);
}
createChart(labels.slice(startIndex, startIndex + displayCount), datasets, function() {
console.log('Diagramm wurde erstellt');
});
}
function createChart(labels, datasets, callback) {
const chartContainer = document.querySelector('.chart-container');
const canvasElement = document.getElementById('myChart');
if (canvasElement) {
chartContainer.removeChild(canvasElement);
}
chartContainer.innerHTML = '<canvas id="myChart"></canvas>';
const ctx = document.getElementById('myChart').getContext('2d');
window.myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: datasets.map((dataset, index) => ({
...dataset,
data: dataset.data.slice(startIndex, startIndex + displayCount),
hidden: !originalDatasetVisibility[index],
})),
},
options: {
scales: {
x: {
stacked: true,
min: labels[startIndex],
max: labels[startIndex + displayCount - 1],
},
y: {},
},
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'x',
}
}
}
}
});
if (callback && typeof callback === 'function') {
callback();
}
window.myChart.resetZoom();
window.myChart.ctx.canvas.addEventListener('wheel', handleZoom);
}
function handleZoom(event) {
const chart = window.myChart;
const chartArea = chart.chartArea;
const originalDatasets = chart.data.datasets;
const zoomEnabled = chart.options.plugins.zoom.zoom.wheel.enabled;
const deltaY = event.deltaY;
if (zoomEnabled && deltaY !== 0) {
const deltaMode = event.deltaMode;
const scaleDelta = deltaY > 0 ? 0.9 : 1.1;
let newMinIndex = chart.getDatasetMeta(0).data.findIndex(
(d) => d.x >= chartArea.left
);
let newMaxIndex = chart.getDatasetMeta(0).data.findIndex(
(d) => d.x >= chartArea.right
);
if (deltaMode === 0) {
newMinIndex = Math.max(0, newMinIndex - Math.abs(deltaY));
newMaxIndex = Math.min(
originalDatasets[0].data.length - 1,
newMaxIndex + Math.abs(deltaY)
);
} else if (deltaMode === 1) {
newMinIndex = Math.max(0, newMinIndex - Math.abs(deltaY) * 10);
newMaxIndex = Math.min(
originalDatasets[0].data.length - 1,
newMaxIndex + Math.abs(deltaY) * 10
);
}
const newMinLabel = originalDatasets[0].data[newMinIndex].label;
const newMaxLabel = originalDatasets[0].data[newMaxIndex].label;
chart.options.scales.x.min = newMinLabel;
chart.options.scales.x.max = newMaxLabel;
chart.update();
}
}
function handleFile(file) {
if (file) {
const reader = new FileReader();
reader.onload = function (e) {
const csvData = e.target.result;
processData(csvData);
};
reader.readAsText(file);
} else {
alert('Bitte eine CSV-Datei auswählen.');
}
}
function handleDrop(event) {
event.preventDefault();
const file = event.dataTransfer.files[0];
handleFile(file);
}
function handleDragOver(event) {
event.preventDefault();
}
function showPreviousData() {
if (startIndex - displayCount >= 0) {
startIndex -= displayCount;
updateChart();
}
}
function showNextData() {
if (startIndex + displayCount < labels.length) {
startIndex += displayCount;
updateChart();
}
}
function updateChart() {
const endIndex = Math.min(startIndex + displayCount, labels.length);
const updatedLabels = labels.slice(startIndex, endIndex);
const updatedDatasets = datasets.map((dataset, index) => ({
...dataset,
data: dataset.data.slice(startIndex, endIndex),
hidden: !originalDatasetVisibility[index],
}));
window.myChart.data.labels = updatedLabels;
window.myChart.data.datasets = updatedDatasets;
window.myChart.options.scales.x.min = updatedLabels[0];
window.myChart.options.scales.x.max = updatedLabels[updatedLabels.length - 1];
window.myChart.update();
}
function removeZoomEventListener() {
window.myChart.ctx.canvas.removeEventListener('wheel', handleZoom);
}
型
2条答案
按热度按时间gojuced71#
这取决于您的用例,数据如何可视化,表示必须有多精确以及需要有多快。
基本上有两个攻击点:“收缩”数据和保持客户端工作负载较低。
这就是说,这里有一些提示,以提高性能:
ChartJ相关 *
Checkout Chartjs official Performance "Tips and Tricks"
在我的本地高数据示例 (~ 100 MB):“禁用动画”,“禁用点绘制”,**“启用跨度间距”和“指定最小和最大的规模”**大幅提高了性能,仅举几例。(但你将不得不调整选项,以获得最佳速度和视觉吸引力)
图1:微小调整~ 70秒(5255999数据行)* x1c 0d1x
图2:微小调整+固定缩放~ 3秒(5255999数据行)*
准备好数据以便于使用
发送解析和排序的json数据,而不是csv,您在客户端手动解析。(文件可能更大,但chartjs不需要在客户端重新解析和规范化数据)
在将数据发送给用户之前聚合/预先计算/清理数据。
您可以加载一小块数据,然后重新加载数据,并更新图表数据,checkout this update chart example
数据/Web端 *
首先分析你的数据
你必须发送所有的列,为一个单一的图表?如果不是删除不需要的列和行。
如上图所示,chartjs“隐藏”了一些值以适应画布上的图表,如果你知道这一点,只需发送 “visible” 值。
如果不需要100%的准确性,删除一些行,让chartjs填充空白。
只需发送所需最大分辨率的数据,将数据拆分为多个文件。特别是如果您只需要特定数据分辨率的子集和/或数据用于不同的图表。
加载数据c,加载后显示为。
**奖励提示:**如果你不需要使用chartjs,请在这里查看this SO question/answer,它建议使用Highcharts来处理bigdata,而不是chartjs。
rn0zuynd2#
在写了我的最后一个答案后,我试图优化你的 (长) 代码,我注意到:
1.上传仅在客户端进行
1.可能主要的问题 (不知道你的数据) 是解析CSV数据。(用我的测试数据集,它需要**> 8秒**,只是解析CSV文件,图表渲染是“快”)
什么是解决方案:
1.如果你能定义文件上传格式,尽量把它做成一个可以马上使用的json文件,用
JSON.parse
左右。1.如果它必须是csv文件,也许你可以改变结构,简单地转置数据,这样你就不必在它上面重复这么多次。(可以说,一行/行是一个数据集)
1.如果这一切都不可能,并且您需要使用该文件类型和结构,您可以将
for
-loops重写为如下内容:字符集
由于在原始代码中,
split
函数的调用次数为 (在我的例子中为1279995次),这是一个相当“昂贵”的操作,因此只需更改循环的顺序就可以将 * 渲染时间从>8秒提高到< 3秒(对于我的测试数据集)*。这里是我的数据集的结果图:
x1c 0d1x的数据