Published
Edited
May 5, 2022
2 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof scale = {
var form = html`<form><input type="range" name="scale" min="0" max="15" value=10 step=1><i>Scale</i></form>`;
form.oninput = () => form.value = form.scale.valueAsNumber;
form.value = 10;
return form;
}
Insert cell
Insert cell
viewof reset_button = Inputs.button("Reset Plot")
Insert cell
viewof copy2sliders = Inputs.button([
["copy to Sliders", value =>value += 1]
], {value: 0})
Insert cell
copy2sliders
Insert cell
Insert cell
labeled_weights
Insert cell
Insert cell
weights
Insert cell
copy2sliders
Insert cell
selected_nodes
Insert cell
updated_weights
Insert cell
weights
Insert cell
mutable counter = 0
Insert cell
copy_to_sliders = {
copy2sliders;
var result = {}
//console.log(selected_nodes.length, copy2sliders)
if (selected_nodes.length >= 2 && copy2sliders !== counter) {
for (let i = 0; i < attrs.length; i++) {
result[attrs[i]] = updated_weights[i];
}
console.log("result>>",result)

mutable counter = copy2sliders
mutable weights = result
}
return result
}
Insert cell
Insert cell
mutable weights = Object.fromEntries(data.attrs.map(a => [a, 1.0]));
Insert cell
weight_list = {
var lst = [];
for(var i = 0; i < attrs.length;i++) {
lst.push(w[attrs[i]]/attrs.length);
}
return lst;
}
Insert cell
attrs = Object.keys(new_data[0])
Insert cell
names = data.map(r => r.Name)
Insert cell
new_data = data.map(r => {
var obj = Object.assign({}, r);
delete obj.Name;
return obj;
});
Insert cell
normalized_data = {
var stdMean = []
for(var i = 0; i < attrs.length; i++){
var obj = {};
var arr = new_data.map(r=>r[attrs[i]]);
obj['std_dev'] = d3.deviation(arr);
obj['mean'] = d3.deviation(arr);
stdMean.push(obj);
}
return new_data.map(r => {
var norm = Object.assign({}, r);
for(var i = 0; i < attrs.length; i++) {
norm[attrs[i]] = (norm[attrs[i]] - stdMean[i]["mean"]) / stdMean[i]["std_dev"];
}
return norm;
})
}
Insert cell
applied_weight_data = {
return normalized_data.map(r => {
for (var i = 0; i < attrs.length; i++){
r[attrs[i]] *= weight_list[i];
}
return r;
});
}
Insert cell
relv_value = {
var relv_list = []
applied_weight_data.map(r => {
var maxx = 0;
for (var i =0; i < attrs.length; i++) {
maxx += r[attrs[i]]
}
relv_list.push(maxx);
})
return relv_list;
}
Insert cell
relevance_inPercent = {
var rele_percentage = [];
var minValue = Math.min(...relv_value);
var maxValue = Math.max(...relv_value);
for (var i =0; i < relv_value.length; i++) {
rele_percentage.push(((relv_value[i] - minValue) / (maxValue - minValue)) * 100);
}
return rele_percentage;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
simulation = d3.forceSimulation(graph.vertices)
.force("link", d3.forceLink(graph.edges)
.distance(e => e.mydistance * 20)
.strength(e => 10.0 / e.distance)
.strength(0.5)
)
//.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
Insert cell
data = {
reset_button;
var data = d3.csvParse(await FileAttachment("Animal_Data_Andromeda@1.csv").text(), d3.autoType);
data.attrs = data.columns.slice(1); // the quantitative attributes in the data table
data.label = data.columns[0]; // the column to use for the dot labels
data.stdevs = {}; // zscore normalization factors for each attribute, for Distance()
data.attrs.forEach(a => data.stdevs[a] = d3.deviation(data, r => r[a]));
data.weights = {};
data.attrs.forEach(a => data.weights[a] = 1.0);
return data;
}
Insert cell
// Distance metric, computes normalized high-dimensional distance between 2 rows in the data table.
// r1, r2 = references to data table rows.
function Distance(r1, r2) {
return d3.sum(data.attrs.map(attr =>
Math.abs(r1[attr] - r2[attr]) / data.stdevs[attr] * data.weights[attr] )); // weighted L1
//return d3.sum(data.attrs.map(attr =>
// Math.abs(r1[attr] - r2[attr]) / data.stdevs[attr] )); // L1
//return Math.sqrt(d3.sum(data.attrs.map(attr =>
// Math.pow((r1[attr] - r2[attr])/data.stdevs[attr], 2)))); // L2
}
Insert cell
graph = {
reset_button;
copy2sliders;
var vertices = data.map( (r,i) => ({'id':r[data.label], 'index':i, 'row':r}) );
var edges = []; // completely connected graph
vertices.forEach( (src, isrc) =>
vertices.forEach( (dst, idst) => {
if(isrc < idst)
edges.push( {'source':src, 'target':dst, 'mydistance':Distance(src.row, dst.row)} ); } ) );
return {'vertices':vertices, 'edges':edges};
}
Insert cell
drag = simulation => {
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;
}*/
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
// .on("end", dragended);
}
Insert cell
Insert cell
width
Insert cell
scale_x = d3.scaleLinear()
.domain([0, width]) // unit: km
.range([-30, 30]) // unit: pixels
Insert cell
scale_y = d3.scaleLinear()
.domain([0, height]) // unit: km
.range([30, -30]) // unit: pixels
Insert cell
Insert cell
md `## Retrieve Selected Data`
Insert cell
mutable selected_nodes = {
reset_button;
return []
}
// add keyword to selected_nodes so that it can be automatically refreshed
Insert cell
selected_data
Insert cell
selected_data = {
let result = [];
for (let i=0; i < selected_nodes.length; ++i) {
let svg = selected_nodes[i]["__data__"]
let index = svg["index"];
//result.push(index)
result.push(data[index]);
}
return result;
}
Insert cell
selected_labels = {
let result = [];
for (let i=0; i < selected_data.length; ++i) {
let x = selected_data[i]['Name'];
result.push(x);
}
return result;
}
Insert cell
FileAttachment("Animal_Data_Andromeda@1.csv").csv()
Insert cell
selected_cords = {
let result = [];
for (let i=0; i < selected_nodes.length; ++i) {
let x = Object.values(selected_nodes[i])[0]['x'];
x = scale_x(x)
let y = Object.values(selected_nodes[i])[0]['y'];
y = scale_y(y)
result.push([x, y]);
}
return result;
}
Insert cell
Insert cell
function euclidean_distance(a, b) {
if (a != null && b != null) {
if (a.length == b.length) {
let result = 0
for (let i = 0; i < a.length; i++) {
let diff = a[i] - b[i]
let square = diff**2
result += square
}
return Math.sqrt(result)
}
}
}
Insert cell
//manhattan_distance([2,2], [1,8])
Insert cell
function manhattan_distance (a, b) {
if (a != null && b != null) {
if (a.length == b.length ) {
let result = 0
for (let i = 0; i < a.length; i++) {
let diff = a[i] - b[i]
let absolute = Math.abs(diff)
result += absolute
}
return result
}
}
}
Insert cell
//euclidean_distance([1,2], [2,5])
Insert cell
//testing_data = [[24.9, 8],[25.1, 6],[24.8, 3.39]]
Insert cell
//pairwise_distance(testing_data)
Insert cell
function pairwise_distance(dataset, distance_function) {
if (selected_cords.length >= 2) {
let row_index = selected_cords.length
let col_index = selected_cords[0].length
let result = []
for (let i = 0; i < row_index; ++i){
let row = []
for (let j = 0; j < row_index; ++j) {
let distance = 0
if (distance_function == "euclidean"){
distance = euclidean_distance(dataset[i], dataset[j])
}
else if (distance_function == "manhattan"){
distance = manhattan_distance(dataset[i], dataset[j])
}
row.push(distance)
}
result.push(row)
}
return result
}
else {
return []
}
}
Insert cell
Insert cell
dataHDpart = {
let result = []
for (let i = 0; i < selected_data.length; i++) {
let row = Object.values(selected_data[i]).slice(1)
result.push(row)
}
return result
}
Insert cell
data2Dnew = {
return selected_cords
}
Insert cell
Insert cell
//1. proposal function
function new_proposal(current, step, direction) {
//console.log(current, step, direction)
let move = current + direction*step*Math.random()
if(move > 0.9999){
move = 0.9999
}
else if (move < 0.00001){
move = 0.00001
}
return move
}
Insert cell
Insert cell
//2. stress function
function stress(distHD, dist2D) {
let diff_sum = 0
let total_sum = 0
for (let i = 0; i < distHD.length; i++){
for (let j = 0; j < distHD[0].length; j++){
let x = distHD[i][j]
let y = dist2D[i][j]
diff_sum += (x-y)**2
total_sum += x**2
}
}
return diff_sum/total_sum
}
Insert cell
//x = [[0, 25, 31.88], [25, 0, 16.28], [31.88, 16.28, 0]]
Insert cell
//y = [[0,2.3,5], [2.31,0,2.76], [5.04, 2.76, 0]]
Insert cell
//stress(x, y)
Insert cell
Insert cell
dist2D = pairwise_distance(data2Dnew, "euclidean")
Insert cell
//testing_data = [[1,2], [3,4]]
Insert cell
//testing_weights = [1, 0.5]
Insert cell
function apply_weights(data, weights){
let result = []
for (let i = 0; i < data.length; i++) {
let row = []
for (let j = 0; j < weights.length; j++) {
row.push(data[i][j]*weights[j])
}
result.push(row)
}
return result
}
Insert cell
function inversMDS(dataHDpart, data2Dnew){
//console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
let MAX = 500;
let col = dataHDpart[0].length;
let row = dataHDpart.length;

let curWeights = Array(col).fill(1/col);
let newWeights = Array(col).fill(1/col);

let flag = Array(col).fill(0);
let direction = Array(col).fill(1);
let step = Array(col).fill(1/col);

let dataHDw = apply_weights(dataHDpart, curWeights);
let distHD = pairwise_distance(dataHDw, "manhattan");
let curStress = stress(distHD, dist2D);
//console.log("cur_stress: " + curStress)
for(let i = 0; i < MAX; ++i){
for (let dim = 0; dim < col; dim++) {
let nw = new_proposal(curWeights[dim], step[dim], direction[dim]);
//console.log(nw)
let s = 1 + nw - curWeights[dim]
for (let index = 0; index < curWeights.length; index++) {
let temp = curWeights[index]/s;
newWeights[index] = curWeights[index]/s;
}
newWeights[dim] = nw/s;

dataHDw = apply_weights(dataHDpart, newWeights);
distHD = pairwise_distance(dataHDw, "manhattan");

let newStress = stress(distHD, dist2D);
//console.log('newstress: '+ newStress + " curstress: "+curStress)
if (newStress < curStress){
let temp = curWeights;
curWeights = newWeights;
newWeights = temp;
curStress = newStress;
flag[dim] = flag[dim] + 1;
}
else {
flag[dim] = flag[dim] - 1;
direction[dim] = -direction[dim];
}
if (flag[dim] >= 5){
step[dim] = step[dim] * 2;
flag[dim] = 0;
}
else if (flag[dim] <= -5) {
step[dim] = step[dim] / 2;
flag[dim] = 0;
}
}
}
//console.log("final stress: " + curStress)
return curWeights;
}
Insert cell
updated_weights = {
var result = []
if (selected_cords.length >= 2) {
result = inversMDS(dataHDpart, data2Dnew)
}
var sum = result.reduce((a, b) => a + b, 0)
for (var i = 0; i < result.length; i++){
result[i] = result[i]*36/sum
}
return result;
}
Insert cell
labeled_weights = {
var result = []
for (let i = 0; i < attrs.length; i++) {
result[i] = {"Name": attrs[i], "Weight": updated_weights[i]}
}
return result
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
FileAttachment("Animal_Data_Andromeda@1.csv").csv()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more