javascript - 将 CSV 解析为一个数组后,在 D3 力图中组合节点和链接(Angular 和 D3.JS)
问题描述
我正在开发一个应用程序,我在 Angular 和 D3.JS 的客户端解析 CSV 文件以创建图形节点
如何组合两个 .csv 文件数组来创建一个数据集?加载 CSV 文件后,数组将返回到 this.files。
我使用的 CSV Parser 库是 NgxCsvParser。
客户端解析器组件
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgxCsvParser } from 'ngx-csv-parser';
import { NgxCSVParserError } from 'ngx-csv-parser';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
files: File[] = [];
connections: any[] = [];
header = true;
nodes: any[] = [];
links: any[] = [];
constructor(
public router: Router,
private ngxCsvParser: NgxCsvParser
) {}
ngOnInit(): void {}
onSelect(event: {
addedFiles: any;
}) {
this.files.push(...event.addedFiles);
this.ngxCsvParser.parse(this.files[0], {
header: this.header,
delimiter: ','
})
.pipe().subscribe((result: Array < any > ) => {
this.connections = result;
this.nodes.push({
"firstName": "You",
"lastName": "",
"name": "You",
"company": ""
});
for (const source of this.connections) {
const sourceName = source["First Name"] + " " + source["Last Name"];
this.links.push({
"source": "You",
"target": sourceName
});
this.nodes.push({
"firstName": source["First Name"],
"lastName": source["Last Name"],
"name": sourceName,
"company": source["Company"]
});
for (const target of this.connections) {
const targetName = target["First Name"] + " " + target["Last Name"];
if (sourceName != targetName && source["Company"] == target["Company"]) {
this.links.push({
"source": sourceName,
"target": targetName
});
}
}
const graph = {
"nodes": this.nodes,
"links": this.links
}
console.log(graph);
localStorage.setItem('graph', JSON.stringify(graph));
}
}, (error: NgxCSVParserError) => {
console.log('Error', error);
});
// this.router.navigateByUrl('/graph');
}
onRemove(event: File) {
console.log(event);
this.files.splice(this.files.indexOf(event), 1);
}
}
d3.组件
import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';
import {Node} from '../d3/models/node';
import {Link} from '../d3/models/link';
@Component({
selector: 'app-graph',
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss']
})
export class GraphComponent implements OnInit {
constructor() { }
ngOnInit() {
const retrievedObject = localStorage.getItem('graph');
const graph = JSON.parse(retrievedObject);
this.loadForceDirectedGraph(graph.nodes, graph.links);
}
loadForceDirectedGraph(nodes: Node[], links: Link[]) {
const svg = d3.select('svg');
const width = +svg.attr('width');
const height = +svg.attr('height');
const color = d3.scaleOrdinal(d3.schemeBlues[9]);
const simulation = d3.forceSimulation()
.force('link', d3.forceLink().id((d: Node) => d.name))// the id of the node
.force("charge", d3.forceManyBody().strength(-5).distanceMax(0.5 * Math.min(width, height)))
.force('center', d3.forceCenter(width / 2, height / 2));
console.log(nodes, links);
const link = svg.append('g')
.attr('class', 'links')
.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('stroke-width', d => Math.sqrt(d.index))
.attr('stroke', 'black');
const node = svg.append('g')
.attr('class', 'nodes')
.selectAll('circle')
.data(nodes)
.enter()
.append('circle')
.attr('r', 8)
.attr("fill", function(d) { return color(d.company); })
.call(d3.drag()
.on('start', dragStarted)
.on('drag', dragged)
.on('end', dragEnded)
);
node.append('text')
.text((d) => d.company)
.attr('x', 6)
.attr('y', 3);
node.append('title').text((d) => d.name);
simulation
.nodes(nodes)
.on('tick', ticked);
simulation.force<d3.ForceLink<any, any>>('link')
.links(links);
function ticked() {
node
.attr('cx', d => d.x)
.attr('cy', d => d.y);
link
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
}
function dragStarted(event) {
if (!event.active) { simulation.alphaTarget(0.3).restart(); }
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragEnded(event) {
if (!event.active) { simulation.alphaTarget(0); }
event.subject.fx = null;
event.subject.fy = null;
}
}
}
解决方案
由于NgxCsvParser
内部使用rxjs
Observables,您可以使用forkJoin
运算符来组合来自多个 Observables 的结果。
import { forkJoin } from "rxjs";
onSelect(event: { addedFiles: any }) {
this.files.push(...event.addedFiles);
const fileOne = this.ngxCsvParser.parse(this.files[0], {
header: this.header,
delimiter: ","
});
const fileTwo = this.ngxCsvParser.parse(this.files[1], {
header: this.header,
delimiter: ","
});
forkJoin(fileOne, fileTwo).subscribe(onResultHandler, onErrorHandler);
}
推荐阅读
- swift - SwiftUI iPad 应用程序在启用多窗口支持时显示黑屏
- css - django-plotly-dash div 尺寸太小,不会改变
- pyspark - 将 numpy (NxM) 矩阵转换为 (Nx1) 具有 N 行和 1 列的具有数组数据的 pyspark 数据帧
- powershell - 在powershell中将日期转换为unix时间戳
- python-3.x - 如何复制一组 s3 密钥并将它们复制到冰川保险库中?
- python - 如何将 MultiIndex Pandas 数据帧转换为 Dask 数据帧
- javascript - Javascript数组未合并
- android - 当我点击我的项目时没有得到任何结果
- ios - 从情节提要上的xib实例化视图上的无限循环
- python - 使用具有特定值计数的 GroupBy 过滤 Pandas DataFrame