Published
Edited
Aug 22, 2021
Insert cell
Insert cell
mutable lastClicked = ""
Insert cell
mutable selectedAgents = []
Insert cell
selectedAgentsString = mutable selectedAgents.sort().join(', ');
Insert cell
Insert cell
percentPlayed = {
let selectedAgentsString = mutable selectedAgents.sort().join(', ');
let svg = d3.create('svg').style("background-color", 'white').attr("preserveAspectRatio", "none") .attr("viewBox", "0 0 963 500")
console.log(selectedAgents)
svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "left")
.attr("y", 2)
.attr("x", 0)
.attr("dy", ".75em")
.attr('style', "font-size: 20px; font-family: 'Bebas Neue';")
.text("Author: Sam Rosenberg").call(addWebFont, 'Bebas Neue', 'https://fonts.gstatic.com/s/bebasneue/v2/JTUSjIg69CK48gW7PXoo9WlhyyTh89Y.woff2').attr('style', d => "font-size: 20px; font-family: 'Bebas Neue';");
svg.append("text")
.attr("class", "signature")
.attr("text-anchor", "left")
.attr("y", 2)
.attr("x", 0)
.attr("dy", ".75em")
.attr('style', "font-size: 20px; font-family: 'Bebas Neue';")
.text(lastClicked).call(addWebFont, 'Bebas Neue', 'https://fonts.gstatic.com/s/bebasneue/v2/JTUSjIg69CK48gW7PXoo9WlhyyTh89Y.woff2').attr('style', d => "font-size: 0; font-family: 'Bebas Neue';");

svg.append("image")
.attr("xlink:href", "https://greblytics.com/wp-content/uploads/2021/03/cropped-Grebnesor-Analytics-SK_JRD-01.png")
.attr("x", 0)
.attr("y", 15)
.attr("width", 160)
.attr("height", 80);
svg.append("text")
.attr("class", "citation")
.attr("text-anchor", "end")
.attr("y", 2)
.attr("x", 963)
.attr("dy", ".75em")
.attr('style', "font-size: 20px; font-family: 'Bebas Neue';")
.text("Data from runitback.gg");
var text2 = svg.append("svg:text").attr("class", "title")
.attr("xlink:href", "greblytics.com")
.attr("text-anchor", "middle")
.attr("y", 60)
.attr("x", 963/2)
.attr('style', "font-size: 45px; font-family: 'Bebas Neue';");
;
text2.append("svg:tspan").style("fill", "black").text("Composition Playrate in Masters 1");

for (let i = 0; i < 5; i++)
{
svg.append("rect")
.attr("x", 180 + 130*i)
.attr("y", 100)
.attr("width", 120)
.attr("height", 120)
.attr("fill", "#eeeeee")
if (selectedAgents.length > i)
{
svg.append("image")
.attr("xlink:href", agents.data.find(a => a.displayName === mutable selectedAgents[i]).displayIcon)
.attr("x", 180 + 130*i)
.attr("y", 100)
.attr("width", 120)
.attr("height", 120);
}
}
//top donut
var radius = 90;
let totalIn = appearsIn(mutable selectedAgents)
var data = {observed: totalIn, not: 652-totalIn}
var color = d3.scaleOrdinal()
.domain(["observed", "not"])
.range(["#98abc5", "#ffffff"])
var pie = d3.pie()
.value(function(d) {return d[1];})
var data_ready = pie(Object.entries(data))
console.log(data_ready)

svg.append("text")
.attr("class", "percent")
.attr("text-anchor", "middle")
.attr("y", 314)
.attr("x", 482)
.attr("dy", ".75em")
.attr('style', "font-size: 55px; font-family: 'Bebas Neue';")
.text(((totalIn/652)*100).toFixed(1) + "%");

svg.append("text")
.attr("class", "label")
.attr("text-anchor", "middle")
.attr("y", 362)
.attr("x", 482)
.attr("dy", ".75em")
.attr('style', "font-size: 15px; font-family: 'Bebas Neue';")
.text("of all comps");

svg.selectAll('whatever')
.data(data_ready)
.join('path').attr('transform','translate(482, 340)')
.attr('d', d3.arc()
.innerRadius(60) // This is the size of the donut hole
.outerRadius(radius)
)
.attr('fill', function(d){console.log(d.data);return(color(d.data[0]))})
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7)
return svg.node();
}
Insert cell
Insert cell
["Map"]
Insert cell
function appearsIn(agentsInput)
{
return d3.sum(fullComps.map(x => agentsInput.every(v => x.includes(v))))
}
Insert cell
appearsIn(["Jett"])
Insert cell
agentId = ["Jett", "Killjoy", "Sage", "Viper", "Breach", "Raze", "Skye", "Cypher", "Sova", "Phoenix", "Brimstone", "Yoru", "Reyna", "Omen", "KAY/O"]
Insert cell
Insert cell
agentSim = {
let nodes = []
for(let i = 0; i < agentId.length; i++){
nodes.push({"id" : agentId[i]});
}
let tempSim = {nodes: nodes,
links: agentLinks}
return tempSim
}
Insert cell
agentSim.links
Insert cell
d3.extent(agentLinks.map(d=> d.value))
Insert cell
{
let lineThickScale = d3.scaleLinear()
.domain(d3.extent(agentLinks.map(d=> d.value)))
.range([1, 4]);
let lineOpacityScale = d3.scaleLinear()
.domain(d3.extent(agentLinks.map(d=> d.value)))
.range([.1, 1]);

let sizeScale = d3.scaleLinear()
.domain([0, 600])
.range([2, 15]);
let svg = d3.create('svg').attr("viewBox", "0 0 963 600");
let simulation = d3.forceSimulation(agentSim.nodes)
.force('links', d3.forceLink(agentSim.links).id(d=>d.id))
.force('charge', d3.forceManyBody().strength(-2000))
.force('center', d3.forceCenter(480,250));
// Make lines and circles for our links and nodes--this should look familiar
let link = svg.selectAll('line')
.data(agentSim.links)
.join('line')
.attr("stroke-width", d => lineThickScale(d.value)).attr("opacity", d => lineOpacityScale(d.value))
.attr('stroke', function(d){
if (d.value !== 0)
{
console.log(d.value)
return 'black'
}
else
{
return "transparent"
}
});
//d => appearsIn(d['id'])
let node = svg.selectAll('circle')
.data(agentSim.nodes)
.join('circle')
.attr('fill', "black")
.attr('r', function(d){
let toReturn = 0
for (let i = 0; i < dataComps.length; i++)
{
if (dataComps[i]["Comp"].includes(d['id']))
{
toReturn++
}
}
return sizeScale(toReturn)
})
.on('mouseover', function(event, d) {
d3.select('#nameText').text(d['id']);
})
.on('mouseout', function(event, d) {
d3.select('#nameText').text('');
});
svg.append('text')
.attr('id', 'nameText')
.attr('x', 10)
.attr('y', 20);
// Define how the links and nodes should be updated at each time step.
// THIS SHOULD LOOK NEW.
simulation.on('tick', function() {
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);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});
// Brief line to make sure that Observable kills the simulation
// if we re-execute this cell later (so that the browser doesn't lag).
invalidation.then(() => simulation.stop());
return svg.node();
}
Insert cell
createLinks()
Insert cell
Insert cell
function getConnected(agentName1, agentName2)
{
let agentsInput = [agentName1, agentName2]
let toReturn = 0;
for (let i = 0; i < dataComps.length; i++)
{
if (dataComps[i]["Comp"].includes(agentName1) && dataComps[i]["Comp"].includes(agentName2))
{
toReturn++
}
}
return toReturn
}
Insert cell
getConnected('Cypher', 'Omen')
Insert cell
dataComps = []
Insert cell
agentComps.map(function(d) {
for (let i = 0; i < d["Picks"]; i++)
{
dataComps.push({"Team": d["Team"], "Map": d["Map"], "Comp" : d["Comp"].split(", ").map(d=>d.replace(/\s+/g, ''))});
console.log("add")
}
})
Insert cell
fullComps = fillFull();
Insert cell
function fillFull()
{
let fullComp = [];
agentComps.map(function(d) {
for (let i = 0; i < d["Picks"]; i++)
{
fullComp.push(d["Comp"].split(", ").map(d=>d.replace(/\s+/g, '')));
console.log("add")
}
})
return fullComp;
}
Insert cell
d3.sum(agentComps.map(d => d["Picks"]))
Insert cell
agentComps = d3.csvParse(await FileAttachment("compositionsmasters1.csv").text())
Insert cell
agents.data.find(a => a.displayName === agentNames[1]).displayIcon
Insert cell
agents = fetch("https://valorant-api.com/v1/agents")
.then(response => {
return response.json();
})
Insert cell
agentNames = [...new Set(agents.data.map(a=>a.displayName))]
Insert cell
agentIcon = [...new Set(agents.data.map(a=>a.displayIcon))]
Insert cell
async function addWebFont(selection, fontName, fontURL, fontType = 'woff2') {
const fontData = await toDataURL(fontURL);
return selection.append('style').text(`
@font-face {
font-family: '${fontName}';
src: url(${fontData}) format('${fontType}');
}
`);
};
Insert cell
async function toDataURL(url) {
return new Promise(async(resolve, reject) => {
const res = await fetch(url);
if(!res.ok) return reject(`Error: ${res.status} ${res.statusText}`);
const blob = await res.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => resolve(reader.result);
});
}
Insert cell
d3 = require('d3@v6')
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