Published
Edited
May 2, 2019
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3tip = require('d3-tip')
Insert cell
vegaEmbed = require("vega-embed@3")
Insert cell
import { vz } from '@gjmcn/vizsla-and-vega-lite'
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// goal_vs_time_data = d3.csv("https://gist.githubusercontent.com/cheung927/bee0838a281f5bb97a9ee220c1c099c6/raw/92f690b03b5aa886bc96c4887bb2d4ec0257df06/goal_vs_time_array.csv")
//Not use
Insert cell
Insert cell
a = d3.csv('https://gist.githubusercontent.com/whtsead213/9e84c3e9194427641d937ea24bb8b253/raw/9449fb763e0e55a5c5595c95b9eb913f7fc63391/merged_event.csv', d => {return {"play_id": d.player_id, "x": d.x, "y": d.y, "to_x": d.to_x, "to_y": d.to_y, "type": d.type, "outcome": d.outcome, "side": d.side, "match_name": d.match_name}})
Insert cell
f1 = function (a) {
var output = [];
a.forEach(function (d) {
if (output.indexOf(d.match_name) === -1) {
output.push(d.match_name);
}
});
return output;
};
Insert cell
data1 = f1(a)
Insert cell
f2 = function(match_name){
var output = [];
match_name.forEach(function (d) {
if (output.indexOf(d) === -1) {
output.push({"match_name": d, children: []});
}
});
return output;
}
Insert cell
data2 = f2(data1)
Insert cell
f3 = function(output, a){
for(var i=0; i<a.length; i++){
if(a[i].type === "1" && a[i].outcome === "1"){
for(var j=0; j<output.length; j++){
if(output[j].match_name == a[i].match_name){
output[j].children.push(a[i]);
}
}
}
}
return output;
}
Insert cell
data = f3(data2, a)
//array: by match
Insert cell
goal_vs_time_data2 = d3.csv("https://gist.githubusercontent.com/cheung927/74bb5a15844ac34e4f822ae31d96ada1/raw/f409cbfd9297dfbd8a48984a4b5c61515734f70c/goal_vs_time_array2.csv")
// col: time , distance, type (miss/post/saved/goals)
// preprocessed data for further use
Insert cell
Insert cell
events_merged_all = d3.csv('https://gist.githubusercontent.com/cheung927/4dfd25af0cfefe2886902902817f5a92/raw/a186a02a5834a84190e12624a5d0a4a94f77f9d7/events_merge_all.csv')
Insert cell
//function for finding the distance
function finddistance (x1,x2,y1,y2){
return Math.pow(Math.pow((x2-x1),2)+Math.pow((y2-y1),2),0.5)
}
Insert cell
r = finddistance(92.5,52.2,100,39.4)
//example
Insert cell
Insert cell
{
let total_goal = 0
let total_miss = 0
let total_post = 0
let total_saved = 0
for (let i = 0; i < events_merged_all.length; i++){
if (events_merged_all[i].type === '16')
total_goal += 1
else if (events_merged_all[i].type === '13')
total_miss += 1
else if (events_merged_all[i].type === '14')
total_post += 1
else if (events_merged_all[i].type === '15')
total_saved += 1
}
return [total_miss, total_post, total_saved, total_goal]
}
Insert cell
function get_distance_array(r) {
let goala = ({
time: [],
distance: []
})
for (let i = 0; i < events_merged_all.length; i++)
if (events_merged_all[i].type === r){
goala.time.push(events_merged_all[i].min)
goala.distance.push(finddistance(events_merged_all[i].x,events_merged_all[i].to_x,events_merged_all[i].y,events_merged_all[i].to_y))
}
return goala
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// {
// for (let i = 0; i < events_merged_all.length; i++){
// if (events_merged_all[i].type === '13'||events_merged_all[i].type === '14' || events_merged_all[i].type === '15'||events_merged_all[i].type === '16'){
// events_merged_all[i].field_pass = finddistance(events_merged_all[i].x,events_merged_all[i].to_x,events_merged_all[i].y,events_merged_all[i].to_y)
// }
// }
// } --------not use----------
Insert cell
{
for (let i = 0; i < events_merged_all.length; i++){
if (events_merged_all[i].side === 'A' && (events_merged_all[i].type === '13'||events_merged_all[i].type === '14' || events_merged_all[i].type === '15'||events_merged_all[i].type === '16')){
events_merged_all[i].x = 100 - events_merged_all[i].x
events_merged_all[i].to_x = 100 - events_merged_all[i].to_x
}
}//convert the away team data to the left
return events_merged_all
}
Insert cell
function typeconvert (x){
if (x === "1") return "Pass"
if (x === "13") return "Miss"
if (x === "14") return "Post"
if (x === "15") return "Saved"
if (x === "16") return "Goal"
}
Insert cell
Insert cell
Insert cell
////all matches, miss(type:13) vs goal(type:16)

chart4 = {
const svg = d3.select(DOM.svg(width + margin.left + margin.right, height + margin.top + margin.bottom));
const pitch = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.right})`)
//tooltip
const tooltip = d3tip()
.style('border', 'solid 3px black')
.style('background-color', 'white')
.style('border-radius', '10px')
.style('float', 'left')
.style('font-family', 'arial')
.html(d => `
Type: ${(typeconvert(d.type))} <br/>
xPosition: ${Math.round(d.x)} <br/>
yPosition: ${Math.round(d.y)} <br/>
Distance to goal : ${Math.round(finddistance(d.x,d.to_x,d.y,d.to_y))} <br/>
</div>`)
// Apply tooltip to our SVG
svg.call(tooltip)
pitch.append('rect')
.attr('x', -margin.left)
.attr('y', -margin.top)
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
//.style('fill', pitchColor)
.style('fill', 'green')
.attr('opacity', 0.7);
//background
const pitchLineData = getPitchLines;
pitch.selectAll('.pitchLines')
.data(pitchLineData)
.enter().append('line')
.attr('x1', d => d['x1'] * pitchMultiplier)
.attr('x2', d => d['x2'] * pitchMultiplier)
.attr('y1', d => d['y1'] * pitchMultiplier)
.attr('y2', d => d['y2'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor);
.style('stroke', 'white');
//all straight line
const shotData2 = events_merged_all;
pitch.selectAll('.pitchLines')
.data(shotData2.filter(function(d){return d.type == 13}))
.enter().append('line')
.attr('x1', d => d['x'] *8.4)
.attr('x2', d => d['to_x'] *8.4 )
.attr('y1', d => d['y'] *5.44 )
.attr('y2', d => d['to_y'] *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'purple')
.attr('opacity', 0.15);
pitch.selectAll('.pitchCircles')
.data(shotData2.filter(function(d){return d.type == 13}))
.enter().append('circle')
.attr('cx', d => d['x'] * 8.4)
.attr('cy', d => d['y'] * 5.44)
.attr('r', d => 5)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'SteelBlue')
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 13: Miss

pitch.selectAll('.pitchLines')
.data(shotData2.filter(function(d){return d.type == 16}))
.enter().append('line')
.attr('x1', d => d['x'] *8.4)
.attr('x2', d => d['to_x'] *8.4 )
.attr('y1', d => d['y'] *5.44 )
.attr('y2', d => d['to_y'] *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'red')
.attr('opacity', 0.15);
pitch.selectAll('.pitchCircles')
.data(shotData2.filter(function(d){return d.type == 16}))
.enter().append('circle')
.attr('cx', d => d['x'] * 8.4)
.attr('cy', d => d['y'] * 5.44)
.attr('r', d => 5)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'GoldenRod')
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 13: Miss
const pitchCircleData = getPitchCircles2;
pitch.selectAll('.pitchCircles')
.data(pitchCircleData)
.enter().append('circle')
.attr('cx', d => d['cx'] * pitchMultiplier)
.attr('cy', d => d['cy'] * pitchMultiplier)
.attr('r', d => d['r'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor)
.style('fill', d => d['color'])
.style('stroke', 'white');
//circle

const pitchArcData = getArcs;
const arc = d3.arc();
pitch.selectAll('.pitchCorners')
.data(pitchArcData)
.enter().append('path')
.attr('d', d => arc(d['arc']))
.attr('transform', d => `translate(${pitchMultiplier * d.x},${pitchMultiplier * d.y})`)
//.style('fill', lineColor)
.style('stroke', 'white');
//curve
return svg.node();
}
Insert cell
Insert cell
////saved (type:15) vs goal(type:16) in all matches

chart5 = {
const svg = d3.select(DOM.svg(width + margin.left + margin.right, height + margin.top + margin.bottom));
const pitch = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.right})`)
//tooltip
const tooltip = d3tip()
.style('border', 'solid 3px black')
.style('background-color', 'white')
.style('border-radius', '10px')
.style('float', 'left')
.style('font-family', 'arial')
.html(d => `
Type: ${(typeconvert(d.type))} <br/>
xPosition: ${Math.round(d.x)} <br/>
yPosition: ${Math.round(d.y)} <br/>
Distance to goal : ${Math.round(finddistance(d.x,d.to_x,d.y,d.to_y))} <br/>
</div>`)
// Apply tooltip to our SVG
svg.call(tooltip)
pitch.append('rect')
.attr('x', -margin.left)
.attr('y', -margin.top)
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
//.style('fill', pitchColor)
.style('fill', 'green')
.attr('opacity', 0.7);
//background
const pitchLineData = getPitchLines;
pitch.selectAll('.pitchLines')
.data(pitchLineData)
.enter().append('line')
.attr('x1', d => d['x1'] * pitchMultiplier)
.attr('x2', d => d['x2'] * pitchMultiplier)
.attr('y1', d => d['y1'] * pitchMultiplier)
.attr('y2', d => d['y2'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor);
.style('stroke', 'white');
//all straight line
const shotData2 = events_merged_all;
pitch.selectAll('.pitchLines')
.data(shotData2.filter(function(d){return d.type == 15}))
.enter().append('line')
.attr('x1', d => d['x'] *8.4)
.attr('x2', d => d['to_x'] *8.4 )
.attr('y1', d => d['y'] *5.44 )
.attr('y2', d => d['to_y'] *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'purple')
.attr('opacity', 0.25);
pitch.selectAll('.pitchCircles')
.data(shotData2.filter(function(d){return d.type == 15}))
.enter().append('circle')
.attr('cx', d => d['x'] * 8.4)
.attr('cy', d => d['y'] * 5.44)
.attr('r', d => 5)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'pink')
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 15: saved

pitch.selectAll('.pitchLines')
.data(shotData2.filter(function(d){return d.type == 16}))
.enter().append('line')
.attr('x1', d => d['x'] *8.4)
.attr('x2', d => d['to_x'] *8.4 )
.attr('y1', d => d['y'] *5.44 )
.attr('y2', d => d['to_y'] *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'red')
.attr('opacity', 0.5);
pitch.selectAll('.pitchCircles')
.data(shotData2.filter(function(d){return d.type == 16}))
.enter().append('circle')
.attr('cx', d => d['x'] * 8.4)
.attr('cy', d => d['y'] * 5.44)
.attr('r', d => 5)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'GoldenRod')
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 16: goal
const pitchCircleData = getPitchCircles2;
pitch.selectAll('.pitchCircles')
.data(pitchCircleData)
.enter().append('circle')
.attr('cx', d => d['cx'] * pitchMultiplier)
.attr('cy', d => d['cy'] * pitchMultiplier)
.attr('r', d => d['r'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor)
.style('fill', d => d['color'])
.style('stroke', 'white');
//circle

const pitchArcData = getArcs;
const arc = d3.arc();
pitch.selectAll('.pitchCorners')
.data(pitchArcData)
.enter().append('path')
.attr('d', d => arc(d['arc']))
.attr('transform', d => `translate(${pitchMultiplier * d.x},${pitchMultiplier * d.y})`)
//.style('fill', lineColor)
.style('stroke', 'white');
//curve
return svg.node();
}
Insert cell
Insert cell
vegaEmbed({
data: { values: goal_vs_time_data2 },
width: 800,
height: 600,
title: 'Goal vs time',
mark: 'point',
encoding: {
x: { field: 'time' },
y: { field: 'distance' },
color: { field: 'type' }
}
})
Insert cell
Insert cell
// chart8 = {
// var data=[
// {x:"total_miss", y:total_miss},
// {x:"total_post", y:total_post},
// {x:"total_saved", y:total_saved},
// {x:"total_goal", y:total_goal},
// ];
// const height = 600
// const svg = d3.select(DOM.svg(width, height))
// const margin = { left: 30, top: 10, right: 10, bottom: 20 }
// const xScale = d3.scaleBand()
// .range([0, width])
// .domain(data.map((s) => s.x))
// .padding(0.5)
// svg.append('g')
// .call(d3.axisBottom(xScale))
// .attr('transform', `translate(0,${height - margin.bottom})`)
// const yScale = d3.scaleLinear()
// .range([height - margin.bottom, margin.top])
// .domain([0,1000])
// svg.append('g')
// .call(d3.axisLeft(yScale))
// .attr('transform', `translate(${margin.left},0)`)

// svg.selectAll('rect')
// .data(data)
// .enter()
// .append('rect')
// .attr('x', d => xScale(d.x))
// .attr('y', d => yScale(d.y))
// .attr('height', d => yScale(0) - yScale(d.y))
// .attr('width', 100)
// .attr('fill', 'SteelBlue')
// return svg.node()
// }
// No use
Insert cell
Insert cell
// Total goal activities in World Cup 2014
vchart1 = vegaEmbed({
data: {
values: [
{"Activity": "total miss","Number": total_miss},
{"Activity": "total post","Number": total_post},
{"Activity": "total saved","Number": total_saved},
{"Activity": "total goal","Number": total_goal},
]
},
encoding: {
y: {"field": "Activity", "type": "ordinal"},
x: {"field": "Number", "type": "quantitative"}
},
layer: [{
mark: "bar"
}, {
mark: {
type: "text",
align: "left",
baseline: "middle",
dx: 3
},
encoding: {
text: {"field": "Number", "type": "quantitative"}
}
}]
})
Insert cell
Insert cell
vchart2 = vegaEmbed({
data: {
values: [
{"Activity": "total miss","Number": total_miss},
{"Activity": "total post","Number": total_post},
{"Activity": "total saved","Number": total_saved},
{"Activity": "total goal","Number": total_goal},
]
},
transform: [{
window: [{
op: "sum",
field: "Number",
as: "TotalNumber"
}],
frame: [null, null]
},
{
calculate: "datum.Number/datum.TotalNumber * 100",
as: "PercentOfTotal"
}],
mark: "bar",
encoding: {
x: {
field: "PercentOfTotal",
type: "quantitative",
axis: {
title: "% of total Shot"
}
},
y: {
field: "Activity",
type: "ordinal",
scale: {
rangeStep: 12
}
}
}
})
Insert cell
Insert cell
WorldCup2014Players = d3.csv('https://gist.githubusercontent.com/wingchu/c44283c2975cbdd32ef65454052d5e56/raw/d2b321163bf98dc50916147e0f121d1475f92243/player.csv', d => {return {"id": d.id, "name": d.name}})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`## Pick a player !!`
Insert cell
Insert cell
Insert cell
Insert cell
data_filter = function (d, selected_match) {
var output = [];
for(var i=0; i<d.length; i++){
if(d[i].match_name == selected_match){
for(var j=0; j<d[i].children.length; j++){
output.push(d[i].children[j]);
}
}
}
return output;
}
Insert cell
data_matched = data_filter(data, selected_match)
Insert cell
Insert cell
md`## 5.1 Pass Events in match: ${selected_match} <br/>`
Insert cell
md`### ${teamsplit[0]} : Blue (Home Team) | ${teamsplit[2]} : Pink (Away Team)`

Insert cell
Insert cell
md`### Using vegaEmbed to show Pass Event coordinate`
Insert cell
md`### ${teamsplit[0]} : Gold (Home Team) | ${teamsplit[2]} : Blue (Away Team)`
Insert cell
vegaEmbed({
data: { values: data_matched },
width: 840,
height: 544,
title: 'Pass Events happened place ',
mark: 'point',
encoding: {
x: { field: 'x' },
y: { field: 'y' },
color: { field: 'side' }
}
})
Insert cell
Insert cell
Insert cell
//selected match

chart13 = {
const svg = d3.select(DOM.svg(width + margin.left + margin.right, height + margin.top + margin.bottom));
const pitch = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.right})`)
//tooltip
const tooltip = d3tip()
.style('border', 'solid 3px black')
.style('background-color', 'white')
.style('border-radius', '10px')
.style('float', 'left')
.style('font-family', 'arial')
.html(d => `
Type: ${(typeconvert(d.type))} <br/>
Name: ${selected_player} <br/>
Home/Away: ${d.side} <br/>
xPosition: ${Math.round(d.x)} <br/>
yPosition: ${Math.round(d.y)} <br/>
Pass Distance : ${Math.round(finddistance(d.x,d.to_x,d.y,d.to_y))} <br/>
</div>`)
// Apply tooltip to our SVG
svg.call(tooltip)
pitch.append('rect')
.attr('x', -margin.left)
.attr('y', -margin.top)
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
//.style('fill', pitchColor)
.style('fill', 'green')
.attr('opacity', 0.7);
//background
svg.append("defs")
.append("marker")
.attr("id", "arrow")
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("refX", 0)
.attr("refY", 3)
.attr("orient", "auto")
.attr("markerUnits", "strokeWidth")
.append("path")
.attr("d", "M0,0 L0,6 L9,3 z")
.attr("fill", "blue");
//arrow
const pitchLineData = getPitchLines;
pitch.selectAll('.pitchLines')
.data(pitchLineData)
.enter().append('line')
.attr('x1', d => d['x1'] * pitchMultiplier)
.attr('x2', d => d['x2'] * pitchMultiplier)
.attr('y1', d => d['y1'] * pitchMultiplier)
.attr('y2', d => d['y2'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor);
.style('stroke', 'white');
//all straight line
pitch.selectAll('.pitchLines')
.data(data_matched.filter(function(d){return (d.side == "H") && (d.play_id == selected_player_id) }))
.enter().append('line')
.attr('x1', d => d.x *8.4)
.attr('x2', d => d.to_x *8.4 )
.attr('y1', d => d.y *5.44 )
.attr('y2', d => d.to_y *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'red')
.attr('opacity', 0.5)
.attr("marker-end", "url(#arrow)");
pitch.selectAll('.pitchCircles')
.data(data_matched.filter(function(d){return (d.side == "H") && (d.play_id == selected_player_id) }))
.enter().append('circle')
.attr('cx', d => d.x * 8.4)
.attr('cy', d => d.y * 5.44)
.attr('r', d => 8)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'SteelBlue')
.attr('opacity', 1)
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 1 , side = home team
pitch.selectAll('.pitchLines')
.data(data_matched.filter(function(d){return (d.side == "A") && (d.play_id == selected_player_id) }))
.enter().append('line')
.attr('x1', d => d.x *8.4)
.attr('x2', d => d.to_x *8.4 )
.attr('y1', d => d.y *5.44 )
.attr('y2', d => d.to_y *5.44 )
.style('stroke-width', lineWidth)
.style('stroke', 'red')
.attr('opacity', 0.75)
.attr("marker-end", "url(#arrow)");
pitch.selectAll('.pitchCircles')
.data(data_matched.filter(function(d){return (d.side == "A") && (d.play_id == selected_player_id) }))
.enter().append('circle')
.attr('cx', d => d.x * 8.4)
.attr('cy', d => d.y * 5.44)
.attr('r', d => 8)
.style('stroke-width', lineWidth)
.style('stroke', lineColor)
.style('fill', d => 'SteelBlue')
.attr('opacity', 1)
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide);
//type 1 , side = away team
const pitchCircleData = getPitchCircles2;
pitch.selectAll('.pitchCircles')
.data(pitchCircleData)
.enter().append('circle')
.attr('cx', d => d['cx'] * pitchMultiplier)
.attr('cy', d => d['cy'] * pitchMultiplier)
.attr('r', d => d['r'] * pitchMultiplier)
.style('stroke-width', lineWidth)
//.style('stroke', lineColor)
.style('fill', d => d['color'])
.style('stroke', 'white');
//circle

const pitchArcData = getArcs;
const arc = d3.arc();
pitch.selectAll('.pitchCorners')
.data(pitchArcData)
.enter().append('path')
.attr('d', d => arc(d['arc']))
.attr('transform', d => `translate(${pitchMultiplier * d.x},${pitchMultiplier * d.y})`)
//.style('fill', lineColor)
.style('stroke', 'white');
//curve
return svg.node();
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more