【Qiita API】いいね数の推移を可視化：時系列データ集計

# やったこと・背景

[Qiita API v2](https://qiita.com/api/v2/docs) を使っていいね数の推移を集計してみました。

仲間内で Qiita での「(・∀・)ｲｲﾈ!」の数を競おうという話があり、その集計作業を自動化するために Qiita API を調べたのがきっかけです。

単に合計を比べても面白くないので、いいね数が推移する様子を可視化してみました。
もし似たような試みがあれば是非活用くださいませ。

他に応用できそうな解析ポイントとしては

- MATLAB からの Web API 利用
- 時系列データの集計・処理のコツ

かと思います。最終成果物はこちら：


![LikesTrend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/911b2994-5bb4-7bd6-328e-3e42476a33bc.png)

@teruqii さん先行逃げ切りタイプ。

２アカウントの比較例ですが、コード自体はそのまま何人でも（リクエスト回数制限内であれば）いけるはずです。


## 環境

MATLAB R2019a (Toolbox 不要)


## 実施手順

1. アクセストークンを取得
2. GET /api/v2/users/:user_id/items で特定ユーザーの記事一覧を取得
3. １日毎のデータとして集計・プロット
4. GET /api/v2/items/:item_id/likes で記事につけられた「いいね！」を作成日時を取得
5. １日毎のデータとして集計・プロット


### 参考にした資料

ありがとうございます！

- [Qiita API v2 ドキュメント](https://qiita.com/api/v2/docs)
- [【Qiita API】いいね！閲覧数の自動集計](https://qiita.com/Naoto9282/items/252c4b386aeafc0052ba)


# アクセストークンを取得

[ユーザの管理画面](https://qiita.com/settings/applications)より、アクセストークンを取得する。スコープは read_qiita だけにしました。

アクセストークン無くてもできる内容なんですが、

> 認証している状態ではユーザごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。

とのことで使った方がいろいろ楽かと思います。

```m:sample_getiteams.m
accessToken = 'Bearer 1234567890abcdef1234567890abcdef1234567890abcdef'; % ダミーTokenです。
opts = weboptions('HeaderFields',{'Authorization',accessToken});
% opts = weboptions; % 使用しない場合

url = "https://qiita.com/api/v2/users/eigs/items";
tmp = webread(url,opts)
```

こんな感じで @eigs (私) の記事別の詳細（投稿日、いいね数、閲覧数）とかいろいろ取得できます。
取得される情報の詳細は [https://qiita.com/api/v2/docs#投稿](https://qiita.com/api/v2/docs#%E6%8A%95%E7%A8%BF) にリストされてますので興味のある方はどうぞ。


# 特定ユーザーの記事一覧を取得（GET /api/v2/users/:user_id/items）

https://qiita.com/api/v2/users のエンドポイントを叩いて記事一覧を取ってきます。

以下のコードを実行しました。

```m:get_items.m

accessToken = 'Bearer 1234567890abcdef1234567890abcdef1234567890abcdef'; % ダミーTokenです。
opts = weboptions('HeaderFields',{'Authorization',accessToken});
% opts = weboptions; % 使用しない場合

% マニュアルで集計対象のアカウント名記載
% 3つ以上でももちろんOK
user_idList = ["teruqii","eigs"]';

item_list = table; % 記事一覧用変数

% user_id 毎に webread 叩きます
for ii=1:length(user_idList)
    user_id = user_idList(ii);
    
    url = "https://qiita.com/api/v2/users/" + user_id + "/items";
    tmp = webread(url,opts);
    
    item_id = string(vertcat(tmp.id)); % item_id を string 型でゲット
    created_at = datetime(vertcat(tmp.created_at),...
        'InputFormat', "uuuu-MM-dd'T'HH:mm:ss'+09:00"); % 作成日時もゲット
    
    N = length(item_id); % 投稿数（下のテーブル型変数を作るのに必要・・）
    tmp = table(created_at, item_id, repelem(user_id,N,1), ones(N,1), ...
        'VariableNames',{'created_at','item_id','user_id','count'});
    item_list = [item_list; tmp]; % ユーザー毎にデータ追加
end 
```

item_list の情報はこんな感じ。teruqii さん、eigs の情報が縦に並んでいます。

![item_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/0d847af0-0bc1-6358-d063-c21560e66a9b.png)

この時点で記事の一覧は取れているので、終了なんですがせっかくなので記事数の推移をプロットしてみます。

成果物はこちら:
![ItemTrend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/84383e37-427a-bb95-69c7-01e8b1adeb07.png)



プロットまでのステップを説明します。item_list のままでは不便なので追加処理：
1. timetable 型への変換
2. unstack で変数分割
3. retime で日ごとデータに変更
4. timerange で集計期間指定
5. cumsum で累積和
6. プロット
を実施します。

```m:get_items.m(続き1)
% まずは timetable 型に変換
titem_list = table2timetable(item_list,'RowTimes','created_at'); 
% user_id 毎に変数をわけます。
unstacked_itemList = unstack(titem_list,'count','user_id');
```

この時点でこんな感じ。

![unstacked_item.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/fa41803b-a2cc-5ebf-dce1-bec0c7986595.png)

```m:get_items.m(続き2)
dailyItems = retime(unstacked_itemList,'daily','count');
trange1 = timerange(datetime(2019,8,1),datetime(2019,9,30));
csItems = varfun(@cumsum,dailyItems(trange1,:));
```

ここからさらに日ごとのデータに変換（dailyItems）して、累積和（csItems）を取りました。

eigs が 2017年に投稿した記事があるので、これは集計期間を 2019/8/1 - 2019/9/30 (trange1)と指定することで除外しています。
![timetables.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/1353c25b-a267-7e8b-3c1a-c4ce91a1064c.png)


そして最後にプロット。

```m:get_items.m(続き3)
plot(csItems.created_at,csItems.Variables,'-o');
grid on
varnames = string(csItems.Properties.VariableNames);
varnames = extractAfter(varnames,"cumsum_");
legend(varnames,'Location','best','FontSize',12);
set(gca,'XTickLabelRotation', 30);
ylabel('Total # of Items');
```


# 記事ごとの「いいね！」作成日時を取得（GET /api/v2/items/:item_id/likes）

https://qiita.com/api/v2/items のエンドポイントを叩いて記事一覧を取ってきます。

以下のコードを実行しました。

```m:get_likes.m

likes_list = table; % いいね一覧用変数
% item_id 毎に webread 叩きます
for ii=1:height(item_list)
    item_id = item_list.item_id(ii); 
    user_id = item_list.user_id(ii);
    
    url = "https://qiita.com/api/v2/items/" + item_id + "/likes";
    tmp = webread(url,'per_page',100,opts); % per_page = 20 (default)
    % デフォルトだと直近20件しか取れないので、100（最大値）で指定。
    % 100以上には対応できません・・（だれかやり方教えてください）
    
    created_at = datetime(vertcat(tmp.created_at),...
        'InputFormat', "uuuu-MM-dd'T'HH:mm:ss'+09:00"); 
    % いいね作成日時（いいねが付いたタイミング）もゲット

    N = length(created_at); % いいね数（下のテーブル型変数を作るのに必要・・）
    tmp = table(created_at, repelem(item_id,N,1), repelem(user_id,N,1), ones(N,1), ...
        'VariableNames',{'created_at','item_id','user_id','count'});
    likes_list = [likes_list; tmp]; % 記事毎にデータ追加
end
```

likes_list の情報はこんな感じ。teruqii さんいいね情報が縦ずらっと並んでいます。下の方には eigs のいいねも並びます。

![likes_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/c46bd714-b130-653a-cc3d-fb8efa406530.png)

ここから後はすでに上で紹介した 
1. timetable 型への変換
2. unstack で変数分割
3. retime で日ごとデータに変更
4. timerange で集計期間指定
5. cumsum で累積和
6. プロット
の流れです。

```m:get_likes.m(続き1)

tlikes_list = table2timetable(likes_list,'RowTimes','created_at'); 
tlikes_listUnstacked = unstack(tlikes_list,'count','user_id');

dailyLikes = retime(tlikes_listUnstacked,'daily','count');
trange1 = timerange(datetime(2019,8,1),datetime(2019,9,30));
csLikes = varfun(@cumsum,dailyLikes(trange1,:));

plot(csLikes.created_at,csLikes.Variables,...
    'LineStyle','-','LineWidth',3,'Marker','x');

varnames = string(csLikes.Properties.VariableNames);
varnames = extractAfter(varnames,"cumsum_");
legend(varnames,'Location','best','FontSize',12);
set(gca,'XTickLabelRotation', 30);
ylabel('Total # of Likes');
```

できあがり！

![LikesTrend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/911b2994-5bb4-7bd6-328e-3e42476a33bc.png)


# まとめ

以下の例を紹介しました

- MATLAB からの Web API 利用
    - QiitaAPI
- 時系列データの集計・処理のコツ
    - timetable 型への変換
    - unstack で変数分割
    - retime で日ごとデータに変更
    - timerange で集計期間指定
    - cumsum で累積和

ゆる募:100件以上のいいね情報を取る方法。webread じゃ難しいかな？