Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
vig
Sublinear Algorithms for VA
pseudo
Commits
3b622bce
Commit
3b622bce
authored
Nov 17, 2020
by
Kruyff,D.L.W. (Dylan)
Browse files
Better visualisations + multivariate data
Former-commit-id:
4f8966d6
parent
af9ce874
Changes
31
Show whitespace changes
Inline
Side-by-side
AngularApp/prototype/src/app/api.service.ts
View file @
3b622bce
...
@@ -6,17 +6,19 @@ export interface RawData {
...
@@ -6,17 +6,19 @@ export interface RawData {
}
}
export
interface
LshData
{
export
interface
LshData
{
candidates
:
number
[];
candidates
:
number
[][][];
distances
:
number
[];
average_candidates
:
number
[];
hash_functions
:
number
[];
// number[][][][];
average_distances
:
number
[];
distances
:
number
[][][];
hash_functions
:
number
[][][][];
parameters
?:
number
[];
parameters
?:
number
[];
}
}
export
interface
TableInfoData
{
export
interface
TableInfoData
{
prototypes
:
{
prototypes
:
{
average
:
number
[];
average
:
number
[]
[]
;
min
:
number
[];
min
:
number
[]
[]
;
max
:
number
[];
max
:
number
[]
[]
;
}[];
}[];
distances
:
number
[][];
distances
:
number
[][];
}
}
...
...
AngularApp/prototype/src/app/labeling-window/labeling-window.component.css
View file @
3b622bce
...
@@ -2,11 +2,12 @@
...
@@ -2,11 +2,12 @@
margin-right
:
20px
;
margin-right
:
20px
;
display
:
flex
;
display
:
flex
;
justify-content
:
center
;
justify-content
:
center
;
border-right
:
1px
solid
;
border-bottom
:
1px
solid
;
}
}
.subplot-container
{
.subplot-container
{
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
overflow-x
:
scroll
;
overflow-x
:
scroll
;
width
:
95%
;
width
:
95%
;
}
}
...
...
AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts
View file @
3b622bce
...
@@ -44,13 +44,13 @@ export class LabelingWindowComponent implements OnInit {
...
@@ -44,13 +44,13 @@ export class LabelingWindowComponent implements OnInit {
}
}
async
showSamples
()
{
async
showSamples
()
{
this
.
topk
=
this
.
state
.
lshData
.
candidates
this
.
topk
=
this
.
state
.
lshData
.
average_
candidates
.
filter
((
candidate
)
=>
this
.
state
.
labels
[
candidate
]
!==
true
)
.
filter
((
candidate
)
=>
this
.
state
.
labels
[
candidate
]
!==
true
)
.
slice
(
0
,
this
.
k
);
.
slice
(
0
,
this
.
k
);
this
.
subplots
=
[];
this
.
subplots
=
[];
const
values
:
number
[][][]
=
await
this
.
state
.
getWindow
(
this
.
topk
);
const
values
:
number
[][][]
=
await
this
.
state
.
getWindow
(
this
.
topk
);
for
(
const
idx
in
this
.
topk
)
{
for
(
const
idx
in
this
.
topk
)
{
const
window
=
values
[
idx
]
.
slice
(
0
,
1
)
;
const
window
=
values
[
idx
];
const
data
=
[];
const
data
=
[];
window
.
forEach
((
channel
:
number
[],
index
:
number
)
=>
{
window
.
forEach
((
channel
:
number
[],
index
:
number
)
=>
{
data
.
push
({
data
.
push
({
...
@@ -70,7 +70,7 @@ export class LabelingWindowComponent implements OnInit {
...
@@ -70,7 +70,7 @@ export class LabelingWindowComponent implements OnInit {
data
:
data
,
data
:
data
,
layout
:
{
layout
:
{
grid
:
{
grid
:
{
rows
:
1
,
//
this.state.queryWindow.length,
rows
:
this
.
state
.
queryWindow
.
length
,
columns
:
1
,
columns
:
1
,
subplots
:
subplots
,
subplots
:
subplots
,
},
},
...
@@ -85,7 +85,7 @@ export class LabelingWindowComponent implements OnInit {
...
@@ -85,7 +85,7 @@ export class LabelingWindowComponent implements OnInit {
b
:
0
,
b
:
0
,
pad
:
4
pad
:
4
},
},
height
:
100
,
height
:
100
*
values
[
0
].
length
,
width
:
300
,
width
:
300
,
titlefont
:
{
titlefont
:
{
size
:
9
size
:
9
...
...
AngularApp/prototype/src/app/labels/labels.component.ts
View file @
3b622bce
...
@@ -20,7 +20,7 @@ export class LabelsComponent implements OnInit {
...
@@ -20,7 +20,7 @@ export class LabelsComponent implements OnInit {
async
createSubplots
()
{
async
createSubplots
()
{
this
.
goodLabels
=
[];
this
.
goodLabels
=
[];
this
.
badLabels
=
[];
this
.
badLabels
=
[];
const
labelWindows
:
number
[][]
=
await
this
.
state
.
getWindow
(
Object
.
keys
(
this
.
state
.
labels
).
map
(
Number
));
const
labelWindows
:
number
[][]
[]
=
await
this
.
state
.
getWindow
(
Object
.
keys
(
this
.
state
.
labels
).
map
(
Number
));
Object
.
keys
(
this
.
state
.
labels
).
forEach
((
key
,
i
)
=>
{
Object
.
keys
(
this
.
state
.
labels
).
forEach
((
key
,
i
)
=>
{
const
index
=
Number
(
key
);
const
index
=
Number
(
key
);
const
plot
=
const
plot
=
...
...
AngularApp/prototype/src/app/main/main.component.html
View file @
3b622bce
...
@@ -3,8 +3,8 @@
...
@@ -3,8 +3,8 @@
<app-overview-window
style=
"z-index: 10"
></app-overview-window>
<app-overview-window
style=
"z-index: 10"
></app-overview-window>
<mat-tab-group
animationDuration=
"0ms"
(selectedTabChange)=
"changeTab($event)"
>
<mat-tab-group
animationDuration=
"0ms"
(selectedTabChange)=
"changeTab($event)"
>
<mat-tab
label=
"Training"
>
<mat-tab
label=
"Training"
>
<app-labeling-window></app-labeling-window>
<app-table-overview></app-table-overview>
<app-table-overview></app-table-overview>
<app-labeling-window></app-labeling-window>
</mat-tab>
</mat-tab>
<mat-tab
label=
"Labeled data"
>
<mat-tab
label=
"Labeled data"
>
<app-labels></app-labels>
<app-labels></app-labels>
...
...
AngularApp/prototype/src/app/overview-window/overview-window.component.html
View file @
3b622bce
<zingchart-angular
#chart
[id]=
"id"
[config]=
"config"
(mousewheel)=
"zoom($event)"
(click)=
"clicked($event)"
[height]=
"
15
0"
></zingchart-angular>
<zingchart-angular
#chart
[id]=
"id"
[config]=
"config"
(mousewheel)=
"zoom($event)"
(click)=
"clicked($event)"
[height]=
"
30
0"
></zingchart-angular>
AngularApp/prototype/src/app/overview-window/overview-window.component.ts
View file @
3b622bce
...
@@ -16,6 +16,7 @@ export class OverviewWindowComponent implements OnInit {
...
@@ -16,6 +16,7 @@ export class OverviewWindowComponent implements OnInit {
public
series
=
[];
public
series
=
[];
public
goodLabels
=
[];
public
goodLabels
=
[];
public
badLabels
=
[];
public
badLabels
=
[];
public
candidateLabels
=
[];
public
data
;
public
data
;
public
layout
;
public
layout
;
...
@@ -93,9 +94,6 @@ export class OverviewWindowComponent implements OnInit {
...
@@ -93,9 +94,6 @@ export class OverviewWindowComponent implements OnInit {
});
});
// Initialize channels
// Initialize channels
this
.
state
.
rawData
.
forEach
((
channel
,
index
)
=>
{
this
.
state
.
rawData
.
forEach
((
channel
,
index
)
=>
{
if
(
index
!==
0
)
{
return
;
}
const
data
=
[];
const
data
=
[];
for
(
let
i
=
0
;
i
<
channel
.
values
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
channel
.
values
.
length
;
i
++
)
{
data
.
push
([
i
,
channel
.
values
[
i
]]);
data
.
push
([
i
,
channel
.
values
[
i
]]);
...
@@ -150,37 +148,59 @@ export class OverviewWindowComponent implements OnInit {
...
@@ -150,37 +148,59 @@ export class OverviewWindowComponent implements OnInit {
};
};
console
.
log
(
this
.
config
);
console
.
log
(
this
.
config
);
console
.
log
(
"
showing plot
"
);
console
.
log
(
"
showing plot
"
);
await
this
.
debugClicked
();
}
}
async
updatePlot
()
{
async
updatePlot
()
{
console
.
log
(
'
updating plot
'
);
if
(
!
this
.
state
.
queryWindow
)
{
return
;
}
this
.
goodLabels
=
[];
this
.
goodLabels
=
[];
this
.
badLabels
=
[];
this
.
badLabels
=
[];
this
.
markers
=
[];
this
.
markers
=
[];
for
(
const
index
in
this
.
state
.
labels
)
{
for
(
const
index
in
this
.
state
.
labels
)
{
if
(
this
.
state
.
labels
[
index
])
{
if
(
this
.
state
.
labels
[
index
])
{
this
.
goodLabels
.
push
([
Number
(
index
)
*
(
12000
/
6
)
,
0
]);
this
.
goodLabels
.
push
([
Number
(
index
),
0
]);
this
.
markers
.
push
({
this
.
markers
.
push
({
type
:
'
area
'
,
type
:
'
area
'
,
// BUG: For some reason the range values are multiplied by 10
// BUG: For some reason the range values are multiplied by 10
range
:
[
Number
(
index
)
*
(
12000
/
6
)
/
10
,
(
Number
(
index
)
*
(
12000
/
6
)
+
this
.
state
.
windowSize
)
/
10
],
range
:
[
Number
(
index
)
/
10
,
(
Number
(
index
)
+
this
.
state
.
windowSize
)
/
10
],
backgroundColor
:
'
#4caf50
'
,
backgroundColor
:
"
#4caf50
"
,
});
});
}
else
{
}
else
{
this
.
badLabels
.
push
([
Number
(
index
)
*
(
12000
/
6
),
0
]);
this
.
badLabels
.
push
([
Number
(
index
)
,
-
1
]);
this
.
markers
.
push
({
this
.
markers
.
push
({
type
:
'
area
'
,
type
:
'
area
'
,
// BUG: For some reason the range values are multiplied by 10
// BUG: For some reason the range values are multiplied by 10
range
:
[
Number
(
index
)
*
(
12000
/
6
)
/
10
,
(
Number
(
index
)
*
(
12000
/
6
)
+
this
.
state
.
windowSize
)
/
10
],
range
:
[
Number
(
index
)
/
10
,
(
Number
(
index
)
+
this
.
state
.
windowSize
)
/
10
],
backgroundColor
:
'
#f44336
'
,
backgroundColor
:
"
#f44336
"
,
});
});
}
}
}
}
this
.
series
[
1
].
values
=
this
.
goodLabels
;
// for (const index of this.state.lshData.average_candidates) {
this
.
series
[
2
].
values
=
this
.
badLabels
;
// this.candidateLabels.push([Number(index), 1]);
this
.
config
.
scaleX
.
markers
=
this
.
markers
;
// this.markers.push({
zingchart
.
exec
(
'
zingchart-ng-1
'
,
'
setdata
'
,
{
// type: 'area',
// // BUG: For some reason the range values are multiplied by 10
// range: [Number(index) / 10, (Number(index) + this.state.windowSize) / 10],
// backgroundColor: '#b1a343',
// });
// }
console
.
log
(
this
.
markers
);
for
(
const
channel
of
this
.
config
.
graphset
)
{
if
(
channel
.
type
===
'
line
'
)
{
channel
.
scaleX
.
markers
=
this
.
markers
;
}
else
{
channel
.
series
[
0
].
values
=
this
.
goodLabels
;
channel
.
series
[
1
].
values
=
this
.
badLabels
;
channel
.
series
[
2
].
values
=
this
.
candidateLabels
;
}
}
this
.
chart
.
setdata
({
data
:
this
.
config
data
:
this
.
config
});
});
}
}
async
updateCandidates
(
sliderIndex
)
{
async
updateCandidates
(
sliderIndex
)
{
...
...
AngularApp/prototype/src/app/progress-view/progress-view.component.css
View file @
3b622bce
...
@@ -27,5 +27,11 @@
...
@@ -27,5 +27,11 @@
justify-content
:
center
;
justify-content
:
center
;
}
}
mat-slider
{
display
:
flex
;
justify-content
:
center
;
width
:
400px
;
}
line
{
stroke
:
#5e4646
;
}
line
{
stroke
:
#5e4646
;
}
circle
{
stroke
:
#fff
;
stroke-width
:
1.5px
;
}
circle
{
stroke
:
#fff
;
stroke-width
:
1.5px
;
}
AngularApp/prototype/src/app/progress-view/progress-view.component.html
View file @
3b622bce
<svg
id=
"visual"
width=
'500'
height=
'300'
></svg>
<!--<svg id="visual" width='500' height='300'></svg>-->
<div
*ngIf=
"data"
class=
"histogram"
>
<div
class=
"container"
>
<plotly-plot
(hover)=
"onHover($event)"
[data]=
"hist.data"
[layout]=
"hist.layout"
></plotly-plot>
</div>
<div
class=
"slider"
>
<mat-slider
[min]=
"0"
[max]=
"maxLength"
step=
"1"
[value]=
"sliderValue"
(input)=
"setSliderValue($event)"
thumbLabel
tickInterval=
"5"
></mat-slider>
</div>
</div>
<div
*ngIf=
"data"
class=
"container"
>
<div
*ngIf=
"data"
class=
"container"
>
<div
class=
"window"
>
<div
class=
"window"
>
<div
class=
"plots"
>
<div
class=
"plots"
>
<plotly-plot
*ngFor=
"let data of this.data; index as i;"
[class.hide]=
"i != sliderValue"
[data]=
"data"
[layout]=
"layout"
></plotly-plot>
<plotly-plot
*ngFor=
"let data of this.data; index as i;"
[class.hide]=
"i != sliderValue"
[data]=
"data"
[layout]=
"layout"
></plotly-plot>
</div>
</div>
</div>
</div>
<mat-slider
vertical
min=
"0"
[max]=
"maxLength"
step=
"1"
[(value)]=
"sliderValue"
thumbLabel
tickInterval=
"5"
></mat-slider>
</div>
</div>
<script
src=
'https://d3js.org/d3.v4.min.js'
></script>
<script
src=
'https://d3js.org/d3.v4.min.js'
></script>
AngularApp/prototype/src/app/progress-view/progress-view.component.ts
View file @
3b622bce
import
{
Component
,
OnInit
}
from
'
@angular/core
'
;
import
{
Component
,
OnInit
,
ViewChild
}
from
'
@angular/core
'
;
import
{
StateService
}
from
'
../state.service
'
;
import
{
StateService
}
from
'
../state.service
'
;
import
*
as
d3
from
'
d3
'
;
import
*
as
d3
from
'
d3
'
;
...
@@ -8,9 +8,11 @@ import * as d3 from 'd3';
...
@@ -8,9 +8,11 @@ import * as d3 from 'd3';
styleUrls
:
[
'
./progress-view.component.css
'
]
styleUrls
:
[
'
./progress-view.component.css
'
]
})
})
export
class
ProgressViewComponent
implements
OnInit
{
export
class
ProgressViewComponent
implements
OnInit
{
@
ViewChild
(
'
chart
'
)
chart
;
public
plot
;
public
plot
;
public
data
;
public
data
;
public
layout
;
public
layout
;
public
hist
;
public
amountOfCandidates
;
public
amountOfCandidates
;
public
hover
=
0
;
public
hover
=
0
;
...
@@ -20,23 +22,60 @@ export class ProgressViewComponent implements OnInit {
...
@@ -20,23 +22,60 @@ export class ProgressViewComponent implements OnInit {
ngOnInit
():
void
{
ngOnInit
():
void
{
this
.
state
.
onNewTableInfo
.
subscribe
(()
=>
{
this
.
showgraph
();
});
this
.
state
.
onNewTableInfo
.
subscribe
(()
=>
{
this
.
showgraph
();
});
this
.
state
.
onNewTableInfo
.
subscribe
(()
=>
{
this
.
showHistogram
();
});
}
}
hoverPlot
(
averages
)
{
showHistogram
()
{
this
.
data
=
averages
.
map
((
prototype
)
=>
{
const
table
=
this
.
state
.
_averageTable
;
return
[
this
.
hist
=
{
{
data
:
[{
x
:
[...
Array
(
prototype
.
average
.
length
).
keys
()],
x
:
Object
.
keys
(
table
),
y
:
prototype
.
average
,
y
:
Object
.
values
(
table
).
map
((
values
:
number
[])
=>
values
.
length
),
// / (this.service.rawValues.length - this.service.windowSize)),
type
:
'
line
'
,
type
:
'
bar
'
,
line
:
{
opacity
:
0.5
,
color
:
'
red
'
,
marker
:
{
width
:
3
color
:
Object
.
keys
(
table
).
map
((
key
)
=>
{
return
this
.
getColor
(
Number
(
key
)
/
Number
(
Object
.
keys
(
table
)[
Object
.
keys
(
table
).
length
-
1
]));
})
}
}
}],
layout
:
{
hovermode
:
'
closest
'
,
autosize
:
true
,
margin
:
{
l
:
10
,
r
:
10
,
t
:
10
,
b
:
10
,
pad
:
4
},
xaxis
:
{
showticklabels
:
false
},
},
{
yaxis
:
{
x
:
[...
Array
(
prototype
.
average
.
length
).
keys
()],
showticklabels
:
false
y
:
prototype
.
max
,
},
height
:
200
,
width
:
400
,
}
};
}
onHover
(
data
)
{
console
.
log
(
data
);
this
.
setSliderValue
({
value
:
data
.
points
[
0
].
x
});
}
hoverPlot
(
averages
)
{
const
subplots
=
[];
this
.
data
=
averages
.
map
((
prototype
)
=>
{
const
channelData
=
[];
prototype
.
max
.
forEach
((
channel
,
index
)
=>
{
channelData
.
push
({
x
:
[...
Array
(
channel
.
length
).
keys
()],
y
:
channel
,
xaxis
:
'
x
'
,
yaxis
:
`y
${
index
+
2
}
`
,
type
:
'
scatter
'
,
type
:
'
scatter
'
,
fill
:
null
,
fill
:
null
,
mode
:
'
lines
'
,
mode
:
'
lines
'
,
...
@@ -44,10 +83,14 @@ export class ProgressViewComponent implements OnInit {
...
@@ -44,10 +83,14 @@ export class ProgressViewComponent implements OnInit {
color
:
'
rgb(55, 128, 191)
'
,
color
:
'
rgb(55, 128, 191)
'
,
width
:
3
width
:
3
}
}
},
});
{
});
x
:
[...
Array
(
prototype
.
average
.
length
).
keys
()],
prototype
.
min
.
forEach
((
channel
,
index
)
=>
{
y
:
prototype
.
min
,
channelData
.
push
({
x
:
[...
Array
(
channel
.
length
).
keys
()],
y
:
channel
,
xaxis
:
'
x
'
,
yaxis
:
`y
${
index
+
2
}
`
,
type
:
'
scatter
'
,
type
:
'
scatter
'
,
fill
:
'
tonexty
'
,
fill
:
'
tonexty
'
,
mode
:
'
lines
'
,
mode
:
'
lines
'
,
...
@@ -55,10 +98,32 @@ export class ProgressViewComponent implements OnInit {
...
@@ -55,10 +98,32 @@ export class ProgressViewComponent implements OnInit {
color
:
'
rgb(55, 128, 191)
'
,
color
:
'
rgb(55, 128, 191)
'
,
width
:
3
width
:
3
}
}
},
];
});
});
});
prototype
.
average
.
forEach
((
channel
,
index
)
=>
{
channelData
.
push
({
x
:
[...
Array
(
channel
.
length
).
keys
()],
y
:
channel
,
xaxis
:
'
x
'
,
yaxis
:
`y
${
index
+
2
}
`
,
type
:
'
line
'
,
line
:
{
color
:
'
red
'
,
width
:
3
}
});
});
return
channelData
;
});
for
(
let
index
=
0
;
index
<
this
.
state
.
queryWindow
.
length
;
index
++
)
{
subplots
.
push
([
`xy
${
index
+
2
}
`
]);
}
this
.
layout
=
{
this
.
layout
=
{
grid
:
{
rows
:
this
.
state
.
queryWindow
.
length
,
columns
:
1
,
subplots
:
subplots
,
},
showlegend
:
false
,
showlegend
:
false
,
hovermode
:
'
closest
'
,
hovermode
:
'
closest
'
,
autosize
:
true
,
autosize
:
true
,
...
@@ -77,15 +142,21 @@ export class ProgressViewComponent implements OnInit {
...
@@ -77,15 +142,21 @@ export class ProgressViewComponent implements OnInit {
zeroline
:
false
,
zeroline
:
false
,
showticklabels
:
false
,
showticklabels
:
false
,
},
},
height
:
300
,
height
:
400
,
width
:
300
,
width
:
400
,
};
this
.
state
.
queryWindow
.
forEach
((
channel
:
number
[],
index
:
number
)
=>
{
this
.
layout
[
`yaxis
${
index
+
2
}
`
]
=
{
zeroline
:
false
,
showticklabels
:
false
,
};
};
});
}
}
public
set
s
liderValue
(
v
:
number
)
{
public
set
S
liderValue
(
v
)
{
this
.
_sliderValue
=
v
;
this
.
_sliderValue
=
v
.
value
;
d3
.
selectAll
(
'
circle
'
).
transition
().
style
(
'
stroke
'
,
undefined
);
d3
.
selectAll
(
'
circle
'
).
transition
().
style
(
'
stroke
'
,
undefined
);
d3
.
select
(
'
#node-
'
+
v
).
transition
().
style
(
'
stroke
'
,
'
black
'
).
style
(
'
stroke-width
'
,
20
);
d3
.
select
(
'
#node-
'
+
v
.
value
).
transition
().
style
(
'
stroke
'
,
'
black
'
).
style
(
'
stroke-width
'
,
20
);
}
}
public
get
sliderValue
():
number
{
public
get
sliderValue
():
number
{
...
@@ -93,11 +164,11 @@ export class ProgressViewComponent implements OnInit {
...
@@ -93,11 +164,11 @@ export class ProgressViewComponent implements OnInit {
}
}
public
get
maxLength
():
number
{
public
get
maxLength
():
number
{
return
Object
.
keys
(
this
.
table
).
length
;
return
Object
.
keys
(
this
.
table
).
length
-
1
;
}
}
public
get
table
()
{
public
get
table
()
{
return
this
.
state
.
t
able
;
return
this
.
state
.
_averageT
able
;
}
}
async
showgraph
()
{
async
showgraph
()
{
...
@@ -107,91 +178,91 @@ export class ProgressViewComponent implements OnInit {
...
@@ -107,91 +178,91 @@ export class ProgressViewComponent implements OnInit {
this
.
hoverPlot
(
this
.
state
.
tableInfo
.
prototypes
);
this
.
hoverPlot
(
this
.
state
.
tableInfo
.
prototypes
);
const
distances
=
this
.
state
.
tableInfo
.
distances
;
const
distances
=
this
.
state
.
tableInfo
.
distances
;
for
(
const
key
in
this
.
table
)
{
//
for (const key in this.table) {
const
size
=
this
.
table
[
key
].
length
;
//
const size = this.table[key].length;
nodes
.
push
({
id
:
key
,
group
:
Number
(
key
),
size
:
size
});
//
nodes.push({id: key, group: Number(key), size: size});
}
//
}
for
(
const
key
in
this
.
table
)
{
//
for (const key in this.table) {
for
(
const
key2
in
this
.
table
)
{
//
for (const key2 in this.table) {
if
(
key
===
key2
)
{
//
if (key === key2) {
continue
;
//
continue;
}
//
}
links
.
push
({
source
:
key
,
target
:
key2
,
value
:
0.001
*
(
100
-
5
*
distances
[
keys
.
indexOf
(
key
)][
keys
.
indexOf
(
key2
)])});
//
links.push({source: key, target: key2, value: 0.001 * (100 - 5 * distances[keys.indexOf(key)][keys.indexOf(key2)])});
}
//
}
}
//
}
const
graph
=
{
nodes
,
links
};
//
const graph = {nodes, links};
//
const
svg
=
d3
.
select
(
'
#visual
'
);
//
const svg = d3.select('#visual');
const
width
=
+
svg
.
attr
(
'
width
'
);
//
const width = +svg.attr('width');