用 Python 和 Chart.js 可视化 GitHub Commits 数据

总感觉从某个时间之后我的 GitHub Commits 数量就一直在下降,但是从官方提供的图片来看并没有那么直观和有说服力,于是萌生出了用另外的方式可视化 GitHub Commits 图像的想法.

整个操作分为数据整理和数据可视化两个部分,对于数据的获取,这里使用了一个公开的服务:GitHub Contributions Chart Generator,这个网站可以根据给定的用户名生成多年的 Commits 图片:

但是图片往往无法直观反馈一个用户的 “日期-Commits 数量” 曲线,所以需要一些特别的处理来对的 Commits 数量进行可视化,思路如下:根据网站提供的 API 查询到所有的 「日期-Commits 数量」 键值对,并且绘制曲线图,加入一些其他需要比对的参数进行图形覆盖,得知在某些时间段内 Commits 的频率情况.

数据整理

首先引入我们需要的一些包:

import json
import requests

获取到 GitHub 上的提交数据:

username = "n0vad3v"
url = "https://github-contributions-api.now.sh/v1/%s" %username

r = requests.get(url)
json_data = json.loads(r.text)

得到的 json_data 数据类似如下:

{
  ...
  "contributions": [
      {
          'date': '2016-04-23', 
          'count': 0, 
          'color': '#ebedf0', 
          'intensity': 0
      },
          {
          'date': '2016-04-22', 
          'count': 0, 
          'color': '#ebedf0', 
          'intensity': 0
      },
    ...
  ]
}

整理数据,一些关键操作写到了注释中,有兴趣的话可以仔细看看:

# 这里准备两个列表,用于最后的文件存放,一个是用于渲染 GitHub 的提交曲线(washed_data_list)
# 另一个是用来渲染从某个时间之后的一个曲线,目前使用常值函数加上 fill 参数用于覆盖(标记时间段)
washed_data_list = list()
t_washed_data_list = list()

# 排除掉不需要的年份
substring_years = ["2017","2016"]
# 从某个日期开始给常值的函数赋值(其他值为 0)
start_date = "2018-11-06"

# 由于时间是从最后到最前的,所以从“无穷远”开始是肯定标记上的,直到到了某个时间点之前就不是了
t_is_known = 1

for item in json_data['contributions']:
    # 排除多个我们不想要的年份
    if not any(x in item['date'] for x in substring_years):
        # 从无穷远循环到我们设定的值开始
        if start_date in item['date']:
            t_is_known = 0
        washed_data = dict()
        washed_data['date'] = item['date']
        washed_data['count'] = item['count']
        washed_data_list.append(washed_data)

        t_washed_data = dict()
        t_washed_data['date'] = item['date']
        if t_is_known == 1:
            # 这里给标记的值标记为 7 ,因为我自己的 GitHub Commits 不很多,太少的话覆盖的部分高度不够,不够明显,太高的话也不合适
            t_washed_data['count'] = 7
        else:
            t_washed_data['count'] = 0
        t_washed_data_list.append(t_washed_data)

# 最后写入文件
with open('data.json','w') as f:
    f.write(json.dumps(washed_data_list))

with open('t_data.json','w') as f:
    f.write(json.dumps(t_washed_data_list))

此时两个文件内的内容部分分别如下:

data.json

t-data.json

可视化数据

使用 Chart.js,部分关键代码如下:

<body>
    <canvas id="chart"></canvas>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<script>
    var gcommit = [];
    var gdate = [];
    var tcommit = [];
    // 引入 GitHub 的 Commits 数据,并且存放入两个 list 中用于后期渲染,gdate 是横轴,gcommit 是纵轴
    $.getJSON("./data.json", function(data) {
        data.forEach(function(item) {
            gcommit.push(item.count);
            gdate.push(item.date);
        })
        // 由于数据是从后往前的,所以我们需要将其反向
        gcommit.reverse();
        gdate.reverse();

        // 这里引入用来覆盖(标记)的数据
        $.getJSON("./t_data.json", function(data) {
            data.forEach(function(item) {
                tcommit.push(item.count);
            })
            // 同样,反向数据
            tcommit.reverse();

            new Chart(document.getElementById("chart"), {
                type: 'line',
                data: {
                    labels: gdate,
                    datasets: [{
                            label: "GitHub Chart",
                            data: gcommit,
                            borderColor: "rgba(179,181,198,1)",
                        },
                        {
                            label: "T-Chart",
                            data: tcommit,
                            borderColor: "#8e5ea2",
                            // 设置覆盖以及覆盖使用的颜色
                            backgroundColor: "#8e5ea2",
                            fill: true
                        }
                    ]
                },
                options: {
                    legend: {
                        display: true
                    },
                    title: {
                        display: true,
                        text: 'GitHub Chart with T-Chart'
                    }
                }
            });
        });
    });
</script>

来看一下结果:

这张图同学认为不很清晰,于是又尝试渲染成了柱状图,如下:

平滑曲线

目前的图像并不 “和谐”,因为我们需要展示和突出的是我们需要的数据的趋势和概况而不是某一天具体的数值(无意义),目前图中完全是折线(其实 Chart.js 已经尽力生成光滑曲线了,但是因为数据中的点实在太多了,所以就被挤成了折线).

由于我们的点实在太多,一般的平滑曲线的方法(例如插值法)并不能满足我们的需求,我们需要的是就地修改数据的方法使图像看上去更加平滑一些,对于这样的处理我们似乎有以下方法:

  1. 引入 “样条曲线” 进行平滑曲线.
  2. 将曲线图横向拉伸,类似嵌套 iframe 让用户可以左右拖动.
  3. 不使用 count 字段进行渲染,而使用 intensity 进行渲染,这样所有的纵轴的值会落在一个相对稳定一些的区间中,换言之,就是给 count 求一个权值.

不过由于时间关系目前并没有对这三种方法中的任何一种加以尝试,有兴趣的读者可以自行尝试,如果可能的话,欢迎留言告诉我最佳方案.

后记

本文给出了针对我自己 GitHub Commits 的可视化图像,继而可以自然地想到,如果将大量数据按照国别/地区分组后进行可视化的话我们又能得出什么样的结果呢?

例如针对中国而言,哪段时间大家 Commit 最积极?哪段时间大家都没有怎么 Commit?如果有了完善的图表加以分析,一定很有意思.

References

  1. sallar/github-contributions-api
  2. Statistics | GitHub Developer Guide