Skip to content

Commit

Permalink
Implement filtering by core author
Browse files Browse the repository at this point in the history
  • Loading branch information
davidnmora authored and davidnmora committed Dec 6, 2017
1 parent 756a850 commit 36e86fa
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 73 deletions.
45 changes: 45 additions & 0 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,51 @@
stroke: black;
}

/* buttons. Citation: Baltazar Gomez on CodePen */
/* Colors */
body {
margin: 0;
background-color: #090812;
font-family: Helvetica, Arial, sans-serif;
color: #fff;
}

#buttons {
text-align: center;
color: #fff;
}

button {
outline: 0;
border: 0;
background: none;
margin: 25px;
padding: 10px 35px;
font-family: 'Montserrat', sans-serif;
font-size: 15px;
color: #fff;
cursor: pointer;
border-radius: 50px;
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
position: relative;

}

button.blue {
color: #22a7f0;
}
button.blue.active,
button.blue:active,
button.blue:hover {
background: #22a7f0;
color: #fff;
border: 2px solid #22a7f0;
}





/* sliders */
.d3-slider-horizontal {
Expand Down
9 changes: 8 additions & 1 deletion data/2-format-graph-data-from-authors-papers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
papersDict = json.loads(f.read())
f.close()

# RETRIEVE CORE AUTHORS
f = open("core-authors-list.json", "r") # 123 articles (each which contain their own citations, refences)
coreAuthors = json.loads(f.read())
f.close()

# FOR SIMPLE TESTING PURPOSES:
# papersDict = { "A-node": {
# "title": "A-node",
Expand Down Expand Up @@ -49,7 +54,9 @@ def nodeAdded(paper):
newNode["influentialCitationCount"] = paper["influentialCitationCount"]
authorsDict = {}
for authorObj in paper["authors"]:
authorsDict[authorObj["name"]] = authorObj["name"] # redundant hash map for fast access when filtering
authorsDict[authorObj["authorId"]] = authorObj["name"] # hash map for fast access when filtering
if authorObj["authorId"] in coreAuthors:
newNode["coreAuthor"] = authorObj["authorId"] # ISSUE: if two core authors on same paper, only list first as singular coreAuthor
newNode["authors"] = authorsDict
nodeDict[glob["updatingIndex"]] = newNode
existingPaperTitles.add(paperTitle)
Expand Down
1 change: 1 addition & 0 deletions data/core-authors-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"2067522": "Carol S. Dweck", "3141129": "Mihaly Csikszentmihalyi", "4426823": "Frederic Luskin", "3990536": "Dacher J Keltner", "6140201": "Emma M Seppala", "3070698": "Daniel T. Gilbert", "1892780": "Barbara L. Fredrickson", "6986158": "Sonja Lyubomirsky", "4858703": "Kristin D. Neff", "4580744": "Robert A. Emmons", "5668084": "Kelly M McGonigal", "24981109": "Daniel J. Siegel", "6262729": "Jon Kabat-Zinn", "6156620": "Jennifer L. Aaker", "4171705": "Corey Lee M. Keyes", "1716527": "Richard J. Davidson", "3084765": "Martin E. P. Seligman", "5720574": "Angela Lee Duckworth", "9031716": "Zindel V. Segal"}
15 changes: 15 additions & 0 deletions data/create-core-authors-JSON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import json
import csv
# IMPORT JSON FILE
authorsDict = {}
with open('core-authors-with-s2ids.csv', 'r') as csvfile:
authorReader = csv.reader(csvfile)
for authorS2Pair in authorReader:
name = authorS2Pair[0]
s2Id = int(authorS2Pair[1])
authorsDict[s2Id] = name


with open("core-authors-list.json", "w") as gd:
gd.write(json.dumps(authorsDict))
gd.close()
2 changes: 1 addition & 1 deletion data/graph-data.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions data/retrieve-key-phrases-and-abstracts-from-s2-file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
keyPhrasesAndAbstractsDict = {}
mySubsetOfPapersTitles = mySubsetOfPapersDict.keys()

with open("all-papers-on-s2-SMALL.json", "r") as allPapersFile:
with open("all-papers-on-s2.json", "r") as allPapersFile:
for line in allPapersFile:
# make json
paper = json.loads(line)
Expand All @@ -22,6 +22,6 @@
}


keyPhrasesAndAbstractsFile = open("key-phrases-and-abstracsts-by-title.json", "w")
keyPhrasesAndAbstractsFile = open("key-phrases-and-abstracts-by-title.json", "w")
keyPhrasesAndAbstractsFile.write(json.dumps(keyPhrasesAndAbstractsDict))
keyPhrasesAndAbstractsFile.close()
9 changes: 6 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
</head>

<body>

<div id="core-authors-list-container"></div>

<button type="button" onclick="filterGraph('toggleCoreAuthors')">Toggle non- Core Authors on and off</button>
<h2>Tree Diameter Range:</h2>
<h2>Positive Psychology Over Time:</h2>
<h3>
max: <span id="DBH-range-slider-max"></span> inches
from: <span id="DBH-range-slider-max"></span>
<br>
min: <span id="DBH-range-slider-min"></span> inches
to: <span id="DBH-range-slider-min"></span>
</h3>
<div id="DBH-range-slider"></div>

Expand Down
170 changes: 107 additions & 63 deletions js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@ console.clear();

// 1. GLOBAL VARIALBES -------------------------------------------------------------------------

const graphDataJSON = "data/graph-data.json";
const graphDataJSON = "data/graph-data.json"
coreAuthorsJSON = "data/core-authors-list.json";

const width = 900,
height = 900,
minRadius = 10, // in pixles
transitionTime = 750, // milliseconds
centeringForce = 0.09,
minYear = 1950,
maxYear = 2017;
maxYear = 2017,
linkColor = "white",
nonCoreAuthorColor = "grey";

let graph, links, link, nodes, node; // globals set within json request response
let graph, links, link, nodes, node, coreAuthorsById; // globals set within json request response

let color = d3.scaleOrdinal(d3.schemeCategory20);
authorColor = {};

let filterParams = {
coreAuthorsOnly: false,
yearRange: { "min": minYear, "max": maxYear }
yearRange: { "min": minYear, "max": maxYear },
authorIsActive: {}
}

var yearToPix = d3.scaleLinear()
Expand All @@ -29,7 +36,6 @@ let svg = d3
.attr("width", width)
.attr("height", height);

let color = d3.scaleOrdinal(d3.schemeCategory20); // TO DO: USE OR DELETE

// 2. SETUP SIMULATION AND SLIDER -------------------------------------------------------------------------

Expand Down Expand Up @@ -64,7 +70,7 @@ d3.select('#DBH-range-slider').call(d3.slider().value([minYear, maxYear]).orient
d3.select('#DBH-range-slider-min').text(minYear);
d3.select('#DBH-range-slider-max').text(maxYear);

// 3. GET DATA AND SETUP INITIAL VIS -------------------------------------------------------------------------
// 3. GET DATA AND SETUP INITIAL VIS DEPENDANT ON DATA -------------------------------------------------------------------------

d3.json(graphDataJSON, function(error, JSONdata) {
if (error) throw error;
Expand All @@ -74,47 +80,74 @@ d3.json(graphDataJSON, function(error, JSONdata) {
links = graph.links;
nodes = graph.nodes;
nodes.forEach(d => { d.x = d.cx = d.y = d.cy = yearToPix(d.year) });


link = svg
.append("g")
.attr("class", "links")
.selectAll("line")
.data(links, d => d.id)
.enter()
.append("line")
.attr("stroke", "black")
.attr("stroke-width", 4);

node = svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes, d => d.id)
.enter()
.append("circle")
.attr("cx", d => d.cx)
.attr("cy", d => d.cy)
.attr("r", d => {
d.degree = links.filter(l => {
return l.source == d.index || l.target == d.index;
}).length;
d.radius = minRadius + (d.degree/2);
return d.radius;

// create core-authors list buttons via a JSON request
d3.json(coreAuthorsJSON, function(error, JSONdata) {
if (error) throw error;
coreAuthorsById = JSONdata;
Object.keys(coreAuthorsById).forEach((authorId, i) => {
authorColor[authorId] = color(i);
filterParams.authorIsActive[authorId] = true;
})
.attr("fill", d => color(1))
.on("mousedown", d => console.log(d))
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);

simulation.nodes(nodes).on("tick", ticked);

simulation.force("link").links(links);
d3.select("#core-authors-list-container")
.selectAll("div")
.data(Object.keys(coreAuthorsById))
.enter()
.append("button")
.html(authorId => coreAuthorsById[authorId])
.attr("class", "core-author-button")
.attr("class", authorId => { makeAuthorActive(authorId, false); return "active"; })
.attr("id" , authorId => "button-" + authorId)
.attr("border", authorId => "2px solid " + authorColor[authorId])
.on("click" , authorId => {
filterParams.authorIsActive[authorId]
? makeAuthorInactive(authorId)
: makeAuthorActive(authorId);
})

link = svg
.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter()
.append("line")
.attr("stroke", linkColor)
.attr("stroke-width", 4);

node = svg
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes, d => d.id)
.enter()
.append("circle")
.attr("cx", d => d.cx)
.attr("cy", d => d.cy)
.attr("r", d => {
d.degree = links.filter(l => {
return l.source == d.index || l.target == d.index;
}).length;
d.radius = minRadius + (d.degree/2);
return d.radius;
})
.attr("fill", d => getCoreAuthorColor(d))
.on("mousedown", d => console.log(d))
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);

simulation.nodes(nodes).on("tick", ticked);

simulation.force("link").links(links);


}) // closes core authors JSON retrieval & button setup

// ____________

Expand All @@ -129,15 +162,11 @@ d3.json(graphDataJSON, function(error, JSONdata) {
.attr("cx", d => d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)))
.attr("cy", d => d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)));
}
}); // closes JSON call
}); // closes graph data JSON call

// 3. HANDLE FILTERING INTERACTIONS -------------------------------------------------------------------------

// setTimeout(function() {
// nodes = nodes.filter(node => node.authors);
// links = links.filter(link => link.source.authors && link.target.authors); # coreAuthors have the author property, non-coreAuthors don't
// restart();
// }, 2500);
const getCoreAuthorColor = (d) => d.coreAuthor ? authorColor[d.coreAuthor] : nonCoreAuthorColor;

const filterGraph = (filterName = false) => {
if (filterName == "toggleCoreAuthors") filterParams.coreAuthorsOnly = !filterParams.coreAuthorsOnly;
Expand All @@ -146,17 +175,32 @@ const filterGraph = (filterName = false) => {
restart();
}

const coreAuthor = (node) => !filterParams.coreAuthorsOnly || node.authors;
const yearInRange = (node) => (node.year >= filterParams.yearRange.min) &&
(node.year <= filterParams.yearRange.max);

const shouldKeepNode = (node) => {
return coreAuthor(node) && yearInRange(node);
// Filter predicates
const coreAuthor = (node) => !filterParams.coreAuthorsOnly || node.authors;
const yearInRange = (node) => (node.year >= filterParams.yearRange.min) &&
(node.year <= filterParams.yearRange.max);
const authorSelected = (node) => !node.coreAuthor ||filterParams.authorIsActive[node.coreAuthor];

// High-level filters
const shouldKeepNode = (node) => coreAuthor(node) && yearInRange(node) && authorSelected(node);
const shouldKeepLink = (link) => shouldKeepNode(link.source) && shouldKeepNode(link.target);

const makeAuthorActive = (authorId, shouldFilterGraphAfter = true) => {
d3.select("#button-" + authorId)
.style("color", "white")
.style("background", authorColor[authorId])
.classed("active", true)
filterParams.authorIsActive[authorId] = true;
if (shouldFilterGraphAfter) filterGraph();
}

const shouldKeepLink = (link) => {
return coreAuthor(link.source) && yearInRange(link.source) &&
coreAuthor(link.target) && yearInRange(link.target);
const makeAuthorInactive = (authorId) => {
d3.select("#button-" + authorId)
.style("color", authorColor[authorId])
.style("background", "none")
.classed("active", false)
filterParams.authorIsActive[authorId] = false;
filterGraph();
}

// 3. UPDATE GRAPH AFTER FILTERING DATA -------------------------------------------------------------------------
Expand All @@ -171,12 +215,12 @@ function restart() {
.remove();

node = node.enter().append("circle")
.attr("fill", function(d) { return color(1); })
.attr("fill", (d) => getCoreAuthorColor(d))
.call(function(node) { node.transition().duration(transitionTime).attr("r", d => d.radius); })
.merge(node);

// Apply the general update pattern to the links.
link = link.data(links, d => d.id);
link = link.data(links);

// Keep the exiting links connected to the moving remaining nodes.
link.exit().transition().duration(transitionTime)
Expand All @@ -189,7 +233,7 @@ function restart() {

link = link.enter().append("line")
.call(function(link) { link.transition().attr("stroke-opacity", 1); })
.attr("stroke", "black")
.attr("stroke", linkColor)
.attr("stroke-width", 4)
.merge(link);

Expand Down
Loading

0 comments on commit 36e86fa

Please sign in to comment.