Skip to content

Instantly share code, notes, and snippets.

@alex-r-bigelow
Last active August 15, 2017 23:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alex-r-bigelow/bce2174cf2ed4a9efd93e65e5d1956cc to your computer and use it in GitHub Desktop.
Save alex-r-bigelow/bce2174cf2ed4a9efd93e65e5d1956cc to your computer and use it in GitHub Desktop.
tool-modality-attributes-and-pca
license: MIT
height: 1500
scrolling: no
border: yes
Modality Usability Threshold Expressiveness Ceiling Number of Tasks Supported Editing vs Starting From Scratch Separation From the Graphics Optimized for Algorithmic vs Immediate Tasks PCA 1 PCA 2
drawing tools 1 4 1 2 2 2 2.5401611 -1.95837756
direct manipulation-based tools 2 1 0 1 2 1 1.70458815 0.61036012
GUI-based tools 0 0 0 0 1 0 0.57795806 2.33751476
grammars 3 2 0 0 0 0 -1.09200032 1.20664758
libraries 4 3 2 1 0 0 -1.48451323 -1.35504455
programming languages 5 5 1 0 0 0 -2.24619376 -0.84110035
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>Tool modalities</title>
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:700" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
<div id="container"></div>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="script.js"></script>
</body>
</html>
import numpy as np
from sklearn.decomposition import PCA
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import scale
%matplotlib inline
#Load data set
data = pd.read_csv('data.csv')
#convert it to numpy arrays
X=data.values[:,1:7]
print 'Raw data:\n', X, '\n'
#Scaling the values
X = scale(X)
pca = PCA(n_components=2)
pca.fit(X)
print 'Components:\n', pca.components_, '\n'
newCoords = pca.transform(X)
print 'Transformed Coordinates:\n', newCoords, '\n'
#The amount of variance that each PC explains
var = pca.explained_variance_ratio_
#Cumulative Variance explains
var1 = np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)
plt.plot(var1)
/* globals d3 */
const radius = 5;
const bigRadius = 4 * radius;
const bigPadding = 3 * bigRadius;
const jitter = 2 * radius;
const padding = 3 * radius;
const scales = {
'Usability Threshold': d3.scaleLinear()
.domain([0, 5])
.range([588 - padding, 490 + padding]),
'Expressiveness Ceiling': d3.scaleLinear()
.domain([0, 5])
.range([490 - padding, 392 + padding]),
'Number of Tasks Supported': d3.scaleLinear()
.domain([0, 2])
.range([392 - padding, 294 + padding]),
'Editing vs Starting From Scratch': d3.scaleLinear()
.domain([0, 2])
.range([294 - padding, 196 + padding]),
'Separation From the Graphics': d3.scaleLinear()
.domain([0, 2])
.range([196 - padding, 98 + padding]),
'Optimized for Algorithmic vs Immediate Tasks': d3.scaleLinear()
.domain([0, 2])
.range([98 - padding, 0 + padding])
};
const pcaScales = {
'x': d3.scaleLinear()
.range([bigPadding, 832 - bigPadding]),
'y': d3.scaleLinear()
.range([bigPadding, 832 - bigPadding])
};
const reverseLabels = {
'drawing tools': true,
'direct manipulation-based tools': true,
'GUI-based tools': false,
'grammars': false,
'libraries': false,
'programming languages': false
};
d3.text('template.svg', template => {
d3.select('#container').html(template);
const background = d3.select('#Background');
const scatterFrame = d3.select('#ScatterFrame');
const scatterGroup = d3.select('svg').append('g');
d3.csv('data.csv', data => {
// Clean the data
data = data.map(row => {
Object.keys(row).forEach(key => {
let floatValue = parseFloat(row[key]);
if (!isNaN(floatValue)) {
row[key] = floatValue;
}
});
return row;
});
// Figure out the PCA domains
pcaScales.x.domain(d3.extent(data, d => d['PCA 1']));
pcaScales.y.domain(d3.extent(data, d => d['PCA 2']));
// Generate a plot for each possible pairing
let pairwisePlots = [];
Object.keys(scales).forEach(xKey => {
Object.keys(scales).forEach(yKey => {
pairwisePlots.push({xKey, yKey});
});
});
let smallMultiples = scatterGroup.selectAll('.smallMultiple').data(pairwisePlots);
smallMultiples.exit().remove();
let smallMultiplesEnter = smallMultiples.enter().append('g')
.classed('smallMultiple', true);
smallMultiples = smallMultiplesEnter.merge(smallMultiples);
// Generate the points in those plots
let modalities = smallMultiples.selectAll('.modality').data((plot, index) => {
// Derive the location of each point in its plot
let locationHashTable = {};
data.forEach(d => {
let derivedPoint = {
Modality: d.Modality,
includeLabel: index === 0,
x: scales[plot.xKey](d[plot.xKey]),
y: scales[plot.yKey](d[plot.yKey]),
pcaX: pcaScales.x(d['PCA 1']),
pcaY: pcaScales.y(d['PCA 2'])
};
if (isNaN(derivedPoint.pcaX) || isNaN(derivedPoint.pcaY)) {
throw new Error();
}
let hash = derivedPoint.x + ',' + derivedPoint.y;
derivedPoint.hash = hash;
if (!locationHashTable[hash]) {
locationHashTable[hash] = [];
}
locationHashTable[hash].push(derivedPoint);
});
// Where there are hash collisions, jitter the points
Object.keys(locationHashTable).forEach(hash => {
let pointsAtLocation = locationHashTable[hash];
if (pointsAtLocation.length > 1) {
let center = hash.split(',').map(d => parseFloat(d));
pointsAtLocation.forEach((point, index) => {
let offset = ((index + 0.5) / pointsAtLocation.length) - 0.5;
point.x = center[0] + offset * jitter;
point.y = center[1] - offset * jitter;
});
}
});
// Finally convert the locationHashTable to an array of points
return Object.keys(locationHashTable).reduce((acc, hash) => {
return acc.concat(locationHashTable[hash]);
}, []);
}, d => d.Modality);
modalities.exit().remove();
let modalitiesEnter = modalities.enter().append('g')
.attr('class', d => 'modality ' + d.Modality.replace(/[ -]/g, ''));
modalities = modalitiesEnter.merge(modalities);
modalitiesEnter.append('circle');
modalitiesEnter.append('text')
.attr('x', d => reverseLabels[d.Modality] ? -1.5 * bigRadius : 1.5 * bigRadius)
.attr('text-anchor', d => reverseLabels[d.Modality] ? 'end' : 'start')
.attr('y', '0.35em')
.text(d => d.includeLabel ? d.Modality : '');
modalities.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
// Okay, now let's deal with animation. First, get the background to match
// the badge
let t = d3.transition()
.delay(10000)
.duration(2500);
background
// .attr('fill', '#000000')
// .transition(t)
.attr('fill', '#333333');
// Show the ScatterFrame
scatterFrame
.attr('opacity', 0)
.transition(t)
.attr('opacity', 1);
// Rotate the scatterGroup
scatterGroup
.attr('transform', 'translate(109,472) rotate(0)')
.transition(t)
.attr('transform', 'translate(525,472) rotate(45)');
// Move all the points into their scatterplot positions
modalities
.attr('transform', d => 'translate(' + d.pcaX + ',' + d.pcaY + ')')
.transition(t)
.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
// Make the points smaller
modalities.select('circle')
.attr('r', bigRadius)
.transition(t)
.attr('r', radius);
// Hide the labels
modalities.select('text')
.attr('opacity', 1)
.transition(t)
.attr('opacity', 0);
});
});
html,
body {
padding: 0px;
margin: 0px;
}
.modality circle {
stroke: rgba(255,255,255,0.8);
stroke-width: 1px;
}
.modality text {
fill: rgba(255,255,255,0.8);
font-family: 'Roboto Slab', 'Helvetica', sans-serif;
font-size: 2em;
font-weight: 700;
}
.drawingtools circle {
fill: #1b9e77;
}
.directmanipulationbasedtools circle {
fill: #d95f02;
}
.GUIbasedtools circle {
fill: #7570b3;
}
.grammars circle {
fill: #e7298a;
}
.libraries circle {
fill: #e6ab02;
}
.programminglanguages circle {
fill: #666666;
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment