球王梅西喜欢在哪里射门?

avatar
数据可视化 @蚂蚁集团

来蹭一个世界杯热点,大家想知道球王梅西最喜欢在哪里射门吗?这个到了我擅长的地方,用 G2 5.0 来数据可视化一下。小手一抖,也可以 star 一下

准备数据

这个网站上有各个球员的所有数据,我们拉下来即可,对于前端来说,这个不难,也不是重点,所以不具体展开了,数据放在这里了。

{
  "id": "32522",
  "minute": "22",
  "result": "MissedShots",
  "X": "0.7859999847412109",
  "Y": "0.52",
  "xG": "0.03867039829492569",
  "player": "Lionel Messi",
  "h_a": "h",
  "player_id": "2097",
  "situation": "OpenPlay",
  "season": "2014",
  "shotType": "LeftFoot",
  "match_id": "5831",
  "h_team": "Barcelona",
  "a_team": "Elche",
  "h_goals": "3",
  "a_goals": "0",
  "date": "2014-08-24 20:00:00",
  "player_assisted": "Rafinha",
  "lastAction": "Pass"
}

可视化看分布

这个数据很全了,有各种数据信息,要知道“梅西喜欢在哪里射门?”这个问题的答案,只需要关注数据中的 x、y 信息即可。快速实现一个散点图,看看位置分布。

import { Chart } from '@antv/g2';

const chart = new Chart({
  container: 'container',
  autoFit: true,
});

chart
  .point()
  .data({
    type: 'fetch',
    value:
      'https://mdn.alipayobjects.com/afts/file/A*FCRjT4NGENEAAAAAAAAAAAAADrd2AQ/messi.json',
  })
  .encode('x', (d) => Number(d.X))
  .encode('y', (d) => Number(d.Y))
  .encode('color', 'shotType')
  .encode('shape', 'point')
  .scale('x', { domain: [0, 1] })
  .scale('y', { domain: [0, 1] })
  .style('fillOpacity', 0.4)
  .axis(false);

chart.render();

这个图已经可以看到一些信息了:梅西是个左撇子,宁愿头球,都不愿意用右脚。

但是这似乎就看不出他喜欢在哪射门,这个时候需要来一个 bin 分箱一下,然后用透明度来展示分箱中的数据量。

import { Chart } from '@antv/g2';

const FW = 600;
const FH = 400;
const P = 50;

const chart = new Chart({
  container: 'container',
  width: FW + P * 2,
  height: FH + P * 2,
  padding: P,
});

chart
  .rect()
  .data({
    type: 'fetch',
    value:
      'https://mdn.alipayobjects.com/afts/file/A*FCRjT4NGENEAAAAAAAAAAAAADrd2AQ/messi.json',
  })
  .transform({
    type: 'bin',
    opacity: 'count',
    thresholdsX: 20,
    thresholdsY: 20,
  })
  .encode('x', (d) => Number(d.X))
  .encode('y', (d) => Number(d.Y))
  .scale('x', { domain: [0, 1] })
  .scale('y', { domain: [0, 1] })

chart.render();

这个图中 x y 都是 0 ~ 1 的数据,应该是指射门位置相对于球场的位置,但是用数字去看是在不方便,如果有一个球场的背景,那就看起来清晰多了。

绘制一个简单的球场

因为足球场,其实是一个绝对布局,场上的标记线都是有标准的规范和比例。

所以在绘制足球场的时候,使用的是一个 G2 的自定义 mark 标记来绘制。

chart
	.shape()
	.style('render', foot);

function foot(styles) {
  return ;
}

这个其实非常繁琐,有点像使用 Canvas 的命令去绘制一个足球场。G 提供了基础的图形去绘制。直接贴最终的代码。

import { Chart } from '@antv/g2';

const FW = 600;
const FH = 400;
const P = 50;

const chart = new Chart({
  container: 'container',
  width: FW + P * 2,
  height: FH + P * 2,
  padding: P,
});

// 足球场
chart
  .shape()
  .style('x', '0%')
  .style('y', '0%')
  .style('render', point)

chart
  .rect()
  .data({
    type: 'fetch',
    value:
      'https://mdn.alipayobjects.com/afts/file/A*FCRjT4NGENEAAAAAAAAAAAAADrd2AQ/messi.json',
  })
  .transform({
    type: 'bin',
    opacity: 'count',
    thresholdsX: 15,
    thresholdsY: 15,
  })
  .encode('x', (d) => Number(d.X))
  .encode('y', (d) => Number(d.Y))
  .scale('x', { domain: [0, 1] })
  .scale('y', { domain: [0, 1] })
  .axis(false);

chart.render();

function point() {
  const {
    canvas: { document },
  } = chart.context();

  const g = document.createElement('g');
  const r = document.createElement('rect', {
    style: {
      x: 0,
      y: 0,
      width: FW,
      height: FH,
      fill: 'green',
      fillOpacity: 0.2,
      stroke: 'grey',
      lineWidth: 1,
    },
  });

  const r1 = document.createElement('rect', {
    style: {
      x: FW - FH * 0.6 * 0.45,
      y: (FH - FH * 0.6) / 2,
      width: FH * 0.6 * 0.45,
      height: FH * 0.6,
      strokeOpacity: 0.5,
      stroke: 'grey',
      lineWidth: 1,
    },
  });

  const r2 = document.createElement('rect', {
    style: {
      x: FW - FH * 0.3 * 0.45,
      y: (FH - FH * 0.3) / 2,
      width: FH * 0.3 * 0.45,
      height: FH * 0.3,
      strokeOpacity: 0.5,
      stroke: 'grey',
      lineWidth: 1,
    },
  });

  const l = document.createElement('line', {
    style: {
      x1: FW / 2,
      y1: 0,
      x2: FW / 2,
      y2: FH,
      strokeOpacity: 0.4,
      stroke: 'grey',
      lineWidth: 2,
    },
  });

  g.append(r);
  g.append(r1);
  g.append(r2);
  g.append(l);

  return g;
}

如果需要更加精细的足球场,可以按照标准的足球场比例规范去绘制。最终效果:

小结

所以梅西喜欢在哪里射门?上图大概能看出来一些信息。复制上述代码到 G2 官网案例中,就可以运行,自己调参了。