※tag1216さんの指摘により、集計期間間違っていることに気付いたので、タイトル等を修正しました。
先日、「Qiitaタグの記事数とフォロワー数を比較してみた」を初投稿した。
投稿後に自身の関連記事を見てみると、、
最上位の記事「Qiitaの投稿をNVD3で可視化してみるとRuby人気なんだなと改めて分かった。」と、グラフの形式が完全に被っていることと、グラフの見やすさも私の方が劣っていた。
正規化や、記事投稿の動機の1つにもなった「#migrated」などオリジナルな観点もあったけど。
そのため、今回は下記、投稿後に思った下記改善を考慮し、完全オリジナルな内容にアップデートする。
- 全数では5年以上のデータとなり、トレンドがわからないため、2018/12~2019/3のデータでグラフを作成する。フォロワーは、集計期間に記事を投稿したユーザのみを対象とする。
- バブルチャートでは3軸の可視化ができるため、いいね数も評価する。
- 傾向からグループを決定してチャートの色分けを行う。
- chart.jsがスマホ環境では崩れていたため、不要なラベルを消してスマホでも結果を見やすくする。
やったこと
- Qiita apiの記事一覧を日付毎に2018/12~2019/3まで取得し、タグ毎の記事数と、タグ毎のいいね数、投稿ユーザ一覧を集計する。
- 上記で取得した全てのユーザについて、フォローしているタグを集計し、タグ毎のフォロワー数を集計する。(今回投稿ユーザが15,178ユーザいたので、1,000回/1hのAPI制限があるので、15時間以上APIを動かし続けてデータを取得した)
- 記事数、フォロワー数、いいね数を、正規化し、同じ重み付けで上位10を決定し、前回の全数のランキングと比較する
- 上位100について、記事数、フォロワー数、いいね数の傾向でタグを19個のグループに分ける
- 上位100について、バブルチャートで、いいね数を大きさで表して、グループ毎に色分けしてグラフ化する。
結果
総タグ数: 15,649,
総記事数: 43,684,
総投稿者数: 15,178,
総タグ記事数: 12,1912,
総タグフォロワー数: 91,046,
総タグいいね数: 1,307,100
集計方法の変更による違い(全数 vs 2018/12~2019/3)
前回記事と順位の差異を含めて下記の通り。
前回記事と比較して、フォロワー数側に大きく変化が生じたのは、フォロワー数をその期間に記事を投稿している人に限っているのが大きいかもしれないが、前回の結果はフォロワー数側に引っ張られて評価値順位が高いものもあったため、今回の修正は妥当(期間を区切った集計の方が有意な結果となる)だと考える。
なお、Railsが評価+8は少し意外だが、それだけRailsのフォロワーに投稿者が多いのではないかと推測する。
タグ名 | 評価値順位 | フォロワー数順位 | 記事数順位 | 評価値(%) | フォロワー数 | 記事数 | 評価値差分(%) |
---|---|---|---|---|---|---|---|
Python | 1(+1) | 1(+1) | 1(-) | 3.35 | 3175 | 3928 | 0.27 |
JavaScript | 2(-1) | 2(-1) | 2(-) | 2.76 | 3106 | 2583 | 1.29 |
Ruby | 3(-) | 4(+10) | 3(-) | 1.72 | 1829 | 1747 | 0.58 |
PHP | 4(-) | 9(-) | 6(-2) | 1.48 | 1642 | 1405 | 0.65 |
Rails | 5(+8) | 15(+14) | 4(+1) | 1.40 | 1355 | 1603 | 0.17 |
Java | 6(-1) | 6(+1) | 12(-3) | 1.34 | 1715 | 972 | 1.09 |
GitHub | 7(+3) | 3(+2) | 30(-) | 1.33 | 2032 | 522 | 1.80 |
Git | 8(+3) | 5(+8) | 21(-7) | 1.28 | 1812 | 689 | 1.43 |
Linux | 9(-1) | 8(+4) | 18(-6) | 1.28 | 1709 | 826 | 1.20 |
Android | 10(-4) | 13(-2) | 14(-6) | 1.19 | 1471 | 927 | 0.86 |
いいね数も含めたTOP100
タグ名 | 評価値順位 | フォロワー数順位 | 記事数順位 | いいね数順位 | 評価値 | フォロワー数 | 記事数 | いいね数 |
---|---|---|---|---|---|---|---|---|
Python | 1 | 1 | 1 | 2 | 3.54 | 3175 | 3928 | 51004 |
JavaScript | 2 | 2 | 2 | 1 | 3.25 | 3106 | 2583 | 55042 |
Ruby | 3 | 4 | 3 | 14 | 1.43 | 1829 | 1747 | 10901 |
PHP | 4 | 9 | 6 | 18 | 1.22 | 1642 | 1405 | 9305 |
Rails | 5 | 15 | 4 | 24 | 1.16 | 1355 | 1603 | 8946 |
HTML | 6 | 7 | 28 | 6 | 1.14 | 1710 | 544 | 14513 |
Git | 7 | 5 | 21 | 22 | 1.08 | 1812 | 689 | 9142 |
Vue.js | 8 | 41 | 13 | 3 | 1.07 | 587 | 966 | 23240 |
Linux | 9 | 8 | 18 | 27 | 1.06 | 1709 | 826 | 8186 |
Java | 10 | 6 | 12 | 35 | 1.04 | 1715 | 972 | 5684 |
GitHub | 11 | 3 | 30 | 36 | 1.03 | 2032 | 522 | 5666 |
CSS | 12 | 11 | 24 | 12 | 1.02 | 1518 | 633 | 11473 |
AWS | 13 | 29 | 5 | 16 | 1.01 | 869 | 1597 | 10088 |
Docker | 14 | 26 | 8 | 8 | 1.01 | 931 | 1187 | 13399 |
Node.js | 15 | 14 | 17 | 20 | 0.99 | 1412 | 851 | 9250 |
Android | 16 | 13 | 14 | 30 | 0.98 | 1471 | 927 | 7521 |
iOS | 17 | 18 | 11 | 9 | 0.98 | 1122 | 975 | 11733 |
機械学習 | 18 | 40 | 20 | 4 | 0.91 | 597 | 765 | 18793 |
C++ | 19 | 24 | 23 | 17 | 0.77 | 978 | 636 | 9339 |
初心者 | 20 | 101 | 9 | 5 | 0.77 | 125 | 1012 | 17543 |
C# | 21 | 23 | 16 | 32 | 0.76 | 994 | 857 | 6222 |
Swift | 22 | 37 | 10 | 29 | 0.72 | 688 | 980 | 7880 |
Go | 23 | 36 | 19 | 26 | 0.69 | 697 | 786 | 8723 |
HTML5 | 24 | 10 | 68 | 82 | 0.69 | 1525 | 225 | 2700 |
Mac | 25 | 21 | 22 | 52 | 0.67 | 1083 | 640 | 3708 |
MySQL | 26 | 16 | 27 | 161 | 0.65 | 1260 | 556 | 1546 |
Python3 | 27 | 62 | 7 | 28 | 0.65 | 314 | 1210 | 7945 |
Qiita | 28 | 12 | 88 | 142 | 0.65 | 1508 | 182 | 1731 |
Unity | 29 | 48 | 15 | 25 | 0.62 | 422 | 864 | 8859 |
DeepLearning | 30 | 52 | 32 | 7 | 0.61 | 372 | 491 | 13471 |
TypeScript | 31 | 49 | 29 | 11 | 0.60 | 417 | 540 | 11608 |
Vim | 32 | 17 | 59 | 53 | 0.58 | 1145 | 260 | 3696 |
React | 33 | 45 | 26 | 19 | 0.58 | 483 | 621 | 9264 |
jQuery | 34 | 19 | 54 | 56 | 0.57 | 1107 | 265 | 3667 |
Firebase | 35 | 65 | 37 | 10 | 0.53 | 303 | 428 | 11687 |
Chrome | 36 | 25 | 86 | 51 | 0.50 | 962 | 183 | 3735 |
iPhone | 37 | 20 | 193 | 123 | 0.48 | 1106 | 84 | 1930 |
新人プログラマ応援 | 38 | 67 | 55 | 13 | 0.46 | 258 | 263 | 11383 |
Xcode | 39 | 32 | 41 | 92 | 0.45 | 779 | 350 | 2536 |
40 | 30 | 92 | 75 | 0.44 | 863 | 170 | 2939 | |
CSS3 | 41 | 22 | 174 | 167 | 0.43 | 1002 | 94 | 1429 |
Ubuntu | 42 | 34 | 38 | 285 | 0.42 | 767 | 427 | 816 |
Laravel | 43 | 63 | 25 | 41 | 0.41 | 309 | 632 | 4765 |
C | 44 | 33 | 52 | 118 | 0.41 | 768 | 266 | 2049 |
kubernetes | 45 | 58 | 31 | 37 | 0.40 | 335 | 494 | 5651 |
ポエム | 46 | 123 | 43 | 15 | 0.39 | 85 | 338 | 10632 |
MacOSX | 47 | 27 | 96 | 567 | 0.39 | 909 | 165 | 332 |
RaspberryPi | 48 | 59 | 34 | 42 | 0.37 | 332 | 463 | 4734 |
CentOS | 49 | 35 | 52 | 215 | 0.37 | 727 | 266 | 1109 |
ShellScript | 50 | 31 | 114 | 575 | 0.36 | 846 | 146 | 329 |
正規表現 | 51 | 28 | 188 | 473 | 0.36 | 877 | 87 | 418 |
Kotlin | 52 | 53 | 35 | 74 | 0.33 | 366 | 446 | 3023 |
自然言語処理 | 53 | 91 | 106 | 23 | 0.32 | 138 | 155 | 9032 |
TensorFlow | 54 | 57 | 55 | 43 | 0.31 | 338 | 263 | 4628 |
Heroku | 55 | 46 | 44 | 119 | 0.31 | 465 | 309 | 2040 |
nuxt.js | 56 | 91 | 44 | 31 | 0.31 | 138 | 309 | 6682 |
VSCode | 57 | 68 | 42 | 40 | 0.30 | 231 | 344 | 4798 |
golang | 58 | 51 | 47 | 73 | 0.30 | 382 | 300 | 3032 |
MachineLearning | 59 | 75 | 58 | 33 | 0.30 | 190 | 262 | 6096 |
SSH | 60 | 38 | 115 | 420 | 0.29 | 656 | 145 | 498 |
Apache | 61 | 39 | 118 | 599 | 0.29 | 654 | 138 | 314 |
個人開発 | 62 | 214 | 167 | 21 | 0.28 | 44 | 99 | 9247 |
nginx | 63 | 42 | 85 | 398 | 0.28 | 578 | 185 | 543 |
Slack | 64 | 69 | 49 | 55 | 0.26 | 228 | 288 | 3688 |
Scala | 65 | 44 | 90 | 214 | 0.26 | 487 | 178 | 1113 |
Django | 66 | 71 | 36 | 111 | 0.25 | 198 | 433 | 2175 |
Rust | 67 | 65 | 79 | 98 | 0.23 | 303 | 198 | 2448 |
Windows | 68 | 124 | 33 | 101 | 0.22 | 84 | 475 | 2403 |
Haskell | 69 | 50 | 156 | 139 | 0.22 | 390 | 105 | 1741 |
gcp | 70 | 83 | 47 | 71 | 0.22 | 149 | 300 | 3095 |
Keras | 71 | 80 | 65 | 54 | 0.22 | 162 | 226 | 3689 |
Emacs | 72 | 47 | 187 | 257 | 0.21 | 457 | 88 | 924 |
docker-compose | 73 | 181 | 64 | 39 | 0.21 | 53 | 242 | 5072 |
数学 | 74 | 89 | 82 | 45 | 0.21 | 139 | 189 | 4394 |
Objective-C | 75 | 43 | 360 | 421 | 0.21 | 504 | 45 | 492 |
SQL | 76 | 98 | 39 | 134 | 0.19 | 129 | 371 | 1797 |
lambda | 77 | 107 | 40 | 126 | 0.19 | 109 | 359 | 1914 |
OpenCV | 78 | 84 | 65 | 77 | 0.19 | 147 | 226 | 2778 |
GoogleAppsScript | 79 | 78 | 63 | 106 | 0.19 | 167 | 243 | 2281 |
Bash | 80 | 98 | 55 | 87 | 0.18 | 129 | 263 | 2581 |
Elixir | 81 | 72 | 74 | 110 | 0.18 | 197 | 205 | 2177 |
Flutter | 82 | 85 | 70 | 93 | 0.18 | 143 | 217 | 2534 |
ネタ | 83 | 500 | 379 | 34 | 0.17 | 14 | 43 | 6037 |
Elm | 84 | 107 | 128 | 60 | 0.17 | 109 | 131 | 3631 |
Zsh | 85 | 55 | 273 | 262 | 0.17 | 348 | 61 | 898 |
競技プログラミング | 86 | 121 | 80 | 68 | 0.17 | 89 | 193 | 3193 |
IoT | 87 | 96 | 59 | 131 | 0.17 | 132 | 260 | 1843 |
MongoDB | 88 | 56 | 202 | 329 | 0.17 | 341 | 82 | 705 |
Azure | 89 | 86 | 46 | 205 | 0.16 | 142 | 301 | 1163 |
データ分析 | 90 | 130 | 109 | 62 | 0.16 | 79 | 154 | 3571 |
設計 | 91 | 262 | 308 | 38 | 0.16 | 33 | 53 | 5262 |
アルゴリズム | 92 | 130 | 124 | 64 | 0.16 | 79 | 134 | 3517 |
UX | 93 | 248 | 188 | 44 | 0.15 | 36 | 87 | 4563 |
初心者向け | 94 | 901 | 106 | 46 | 0.15 | 6 | 155 | 4235 |
GoogleCloudPlatform | 95 | 93 | 76 | 130 | 0.15 | 135 | 202 | 1848 |
pandas | 96 | 146 | 74 | 79 | 0.15 | 66 | 205 | 2754 |
R | 97 | 77 | 78 | 192 | 0.15 | 170 | 200 | 1268 |
PostgreSQL | 98 | 94 | 51 | 287 | 0.15 | 134 | 280 | 801 |
api | 99 | 199 | 68 | 89 | 0.14 | 47 | 225 | 2572 |
AI | 100 | 194 | 119 | 65 | 0.14 | 48 | 136 | 3501 |
グループの決定
~~多次元データ分析に全く詳しくないため、~~直感的にフォロワー数/記事数/いいね値数の一部が多いもの、一部が少ないもので下記の(6 * 3) + 1 = 19グループに分ける
※判定は+や-数が多い順に行う
グループ名 | 色 | 判定内容 | タグ(TOP100内) | 補足 |
---|---|---|---|---|
いいね+++ | rgba(255, 0, 0, 0.5) |
いいねの割合が一番高く、 他のポイントの2.5倍以上 |
DeepLearning Firebase 新人プログラマ応援 ポエム 自然言語処理 個人開発 ネタ 設計 UX 初心者向け |
記事数/フォロワー数の割にいいねが多い? |
いいね++ | rgba(191, 0, 0, 0.4) |
いいねの割合が一番高く、 他のポイントの1.6倍以上 |
Vue.js 機械学習 TypeScript nuxt.js MachineLearning 数学 Elm データ分析 アルゴリズム AI |
記事数/フォロワー数の割にいいねが多い? |
いいね+ | rgba(127, 0, 0, 0.3) |
いいねの割合が一番高く、 他のポイントの1.1倍以上 |
Python React VSCode Slack Keras OpenCV |
記事数/フォロワー数の割にいいねが多い? |
フォロワー+++ | rgba(0, 0, 255, 0.5) |
フォロワーの割合が一番高く、 他のポイントの2.5倍以上 |
Git Linux GitHub HTML5 MySQL Qiita Vim jQuery Chrome iPhone Xcode CSS3 C MacOSX CentOS ShellScript 正規表現 SSH Apache nginx Scala Haskell Emacs Objective-C Zsh MongoDB |
記事数/記事内容共に不足している? |
フォロワー++ | rgba(0, 0, 191, 0.4) |
フォロワーの割合が一番高く、 他のポイントの1.6倍以上 |
HTML Java CSS Node.js Android Mac Heroku golang Rust |
記事数/記事内容共に不足している? |
フォロワー+ | rgba(0, 0, 127, 0.3) |
フォロワーのポイントが一番高く、 他のポイントの1.1倍以上 |
iOS C++ C# Go Elixir |
記事数/記事内容共に不足している? |
記事数+++ | rgba(0, 255, 0, 0.5) |
記事数の割合が一番高く、 他のポイントの2.5倍以上 |
- | フォロワーが少なく、いいねがもらいにくい? |
記事数++ | rgba(0, 191, 0, 0.4) |
記事数の割合が一番高く、 他のポイントの1.6倍以上 |
Python3 Django Windows SQL lambda |
フォロワーが少なく、いいねがもらいにくい? |
記事数+ | rgba(0, 127, 0, 0.3) |
記事数の割合が一番高く、 他のポイントの1.1倍以上 |
AWS Laravel IoT GoogleCloudPlatform |
フォロワーが少なく、いいねがもらいにくい? |
いいね--- | rgba(0, 255, 255, 0.4) |
いいねの割合が一番低く、 他のポイントの0.4倍以下 |
Ubuntu | 記事数、フォロワー数は比較的多いが、いいねがつきにくい? |
いいね-- | rgba(0, 191, 191, 0.3) |
いいねの割合が一番低く、 他のポイントの0.625倍以下 |
Ruby PHP Rails Azure R PostgreSQL |
記事数、フォロワー数は比較的多いが、いいねがつきにくい? |
いいね- | rgba(0, 127, 127, 0.2) |
いいねの割合が一番低く、 他のポイントの0.9倍以下 |
Swift Kotlin |
記事数、フォロワー数は比較的多いが、いいねがつきにくい? |
フォロワー--- | rgba(255, 255, 0, 0.4) |
フォロワーの割合が一番低く、 他のポイントの0.4倍以下 |
初心者 docker-compose api |
フォロワー数は比較的少ないが、記事は多く、いいねもついている? |
フォロワー-- | rgba(191, 191, 0, 0.3) |
フォロワーの割合が一番低く、 他のポイントの0.625倍以下 |
競技プログラミング pandas |
フォロワー数は比較的少ないが、記事は多く、いいねもついている? |
フォロワー- | rgba(127, 127, 0, 0.2) |
フォロワーの割合が一番低く、 他のポイントの0.9倍以下 |
Unity kubernetes gcp Bash Flutter |
フォロワー数は比較的少ないが、記事は多く、いいねもついている? |
記事数--- | rgba(255, 0, 255, 0.4) |
記事数の割合が一番低く、 他のポイントの0.4倍以下 |
- | 記事数は比較的少ないが、フォロワーは多く、いいねもついている? |
記事数-- | rgba(191, 0, 191, 0.3) |
記事数の割合が一番低く、 他のポイントの0.625倍以下 |
JavaScript TensorFlow |
記事数は比較的少ないが、フォロワーは多く、いいねもついている? |
記事数- | rgba(127, 0, 127, 0.2) |
記事数の割合が一番低く、 他のポイントの0.9倍以下 |
- | 記事数は比較的少ないが、フォロワーは多く、いいねもついている? |
バランス | rgba(0, 0, 0, 0.4) |
上記以外 分散が近い |
Docker RaspberryPi GoogleAppsScript |
- |
グラフ化
縦軸が「記事数」、横軸が「フォロワー数」、円の大きさを「いいね数」で表して、上記のグループ色でグラフ化しました。
See the Pen qiita_tag_score_v2 by j5c8k6m8 (@j5c8k6m8) on CodePen.
tooltipとかスケールとか頑張ればもう少し何とかなりますが、疲れたので。
考察等
- グラフで見ると、「Python」と「JavaScript」が圧倒的。JavaScriptはフォロワー数やいいね数に比べると記事が不足気味
- 記事数よりもいいね数のほうが顕著にトレンドを表しているように見える。2018/12~2019/3でいうとDeepLearning,Firebase,自然言語処理,Vue.js,機械学習,TypeScript,nuxt.js,Elmあたり
- RUby,Rails,PHPは安定し記事数やフォロワー数に対して「いいね」はつきにくい。
- AWSは記事数は多いが、いいね数は少ない
- gitを含めフォロワー数が多いが、記事数といいね数が少ない安定した領域が多い←これはPythonやJavaScriptにデータが引っ張られている要因もあるかも
コード
使用した全コードはこちら
汚いので参考まで
const moment = require('moment')
const DATE_PIRIOD = [ '2018-12-01', '2019-03-31' ];
let request = require('request');
let fs = require("fs");
let headers = {
'Authorization': 'Bearer 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd' // token
};
let itesm = [];
let users = [];
function getDayItems(argMoment, cnt, callback) {
let options = {
url: `https://qiita.com/api/v2/items?page=${cnt}&per_page=100&query=created:<${argMoment.format('YYYY-MM-DD')}`,
headers: headers
}
request(options, function(error, response, body) {
if (error) {
return console.error('ERROR!', error);
} else {
let subtractDate = moment(argMoment.format('YYYY-MM-DD') + 'T09:00:00+09:00').subtract(1, 'days');
console.log(subtractDate.format('YYYY-MM-DD'));
let tmp = JSON.parse(body).filter( v => subtractDate.isSameOrBefore(v.created_at));
users = users.concat(tmp.map( v => v.user.id));
itesm = itesm.concat(tmp.map( v => { return { id: v.id, likes_count: v.likes_count, tags: v.tags, created_at: v.created_at}; }));
if (tmp.length === 100) {
setTimeout( () => getDayItems(argMoment, cnt + 1, callback), 3600); // 3,600秒 / 1,000回
} else {
callback();
}
}
});
}
function getPeriod(target_moment, end_moment) {
if (end_moment.isSameOrBefore(target_moment)) {
getDayItems(target_moment, 1, () => getPeriod(target_moment.subtract(1, 'days'), end_moment));
} else {
fs.writeFile('itesm.json', JSON.stringify(itesm) , (err, data) => {});
fs.writeFile('users.json', JSON.stringify(users.filter((x, i, self) => self.indexOf(x) === i)), (err, data) => {});
fs.writeFile('created_at.list', itesm.map( v => v.created_at ).join('\n') , (err, data) => {});
}
}
getPeriod(moment(DATE_PIRIOD[1]).add(1, 'days'), moment(DATE_PIRIOD[0]).add(1, 'days'));
let request = require('request');
let fs = require("fs");
let headers = {
'Authorization': 'Bearer 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd' // token
};
let tags = [];
let followers = [];
function getFollowers(user_id) {
return new Promise( (resolve) => {
let options = {
url: `https://qiita.com/api/v2/users/${user_id}/following_tags`,
headers: headers
}
request(options, function(error, response, body) {
if (error) {
return console.error('ERROR!', error);
} else {
let followers = JSON.parse(body);
fs.writeFile(`./followers/${user_id}.json`, JSON.stringify(followers) , (err, data) => {});
setTimeout( () => resolve(followers), 3600); // 3,600 * 1000秒 / 1,000回
}
});
});
}
function getFollowersByFile(path) {
return new Promise( (resolve) => {
fs.readFile(path, 'utf8', function (err, text) {
let followers = JSON.parse(text);
resolve(followers);
});
});
}
(async () => {
fs.readFile('./users.json', 'utf8', async (err, text) => {
let user = JSON.parse(text);
for (let i = 0; i < user.length; i++) {
let follower_tags;
try {
fs.statSync(`./followers/${user[i]}.json`);
follower_tags = await getFollowersByFile(`./followers/${user[i]}.json`);
} catch(err) {
follower_tags = await getFollowers(user[i]);
}
tags.push(follower_tags);
console.log(tags.length);
}
fs.writeFile('followers.json', JSON.stringify(tags) , (err, data) => {});
});
})()
let fs = require("fs");
let tag_Hash = {};
let tags = [];
fs.readFile('./items.json', 'utf8', function (err, text) {
let items = JSON.parse(text);
items.forEach(item => {
item.tags.forEach( tag => {
if (tag.name) {
if (!tag_Hash[tag.name]) {
tag_Hash[tag.name] = { id: tag.name, items_count: 0, followers_count: 0, likes_count: 0 };
tags.push(tag_Hash[tag.name]);
}
tag_Hash[tag.name].items_count += 1;
tag_Hash[tag.name].likes_count += item.likes_count;
}
});
});
fs.readFile('./followers.json', 'utf8', function (err, text) {
let followers = JSON.parse(text);
followers.forEach(follower_tags => {
if (follower_tags.type !== 'not_found') {
follower_tags.forEach( tag => {
if (!tag_Hash[tag.id]) {
tag_Hash[tag.id] = { id:tag.id, items_count: 0, followers_count: 0, likes_count: 0 };
tags.push(tag_Hash[tag.id]);
}
tag_Hash[tag.id].followers_count += 1;
});
}
});
fs.writeFile('tags.json', JSON.stringify(tags) , (err, data) => {});
});
});
// tags.jsonにデータが配置している前提
const OUTPUT_RANKING_NUM = 100;
const WEIGHT = { item_count: 1, followers_count: 1, likes_count: 1,};
let fs = require("fs");
const set_rank = (arr, attr, sort_flg) => {
let target = sort_flg ? arr : arr.slice();
target.sort( (a, b) => b[attr] - a[attr] );
let tmp_ranking = 0;
let tmp_score = 9999999999;
target.forEach( (v, i) => {
if (v[attr] < tmp_score) {
tmp_score = v[attr];
tmp_ranking = i + 1;
}
v[`${attr}_rank`] = tmp_ranking;
})
}
const get_ranked_json = (tags, num, weight) =>{
let total_items_count = tags.reduce( (s, v) => v.items_count + s, 0)
let total_followers_count = tags.reduce( (s, v) => v.followers_count + s, 0)
let total_likes_count = tags.reduce( (s, v) => v.likes_count + s, 0)
console.log(`総タグ数: ${tags.length}, 総記事数: ${total_items_count}, 総フォロワー数: ${total_followers_count}, 総いいね数: ${total_likes_count}`);
tags = JSON.parse(JSON.stringify(tags)); // 属性を書き換えるのでオブジェクトをコピーする
set_rank(tags, 'items_count');
set_rank(tags, 'followers_count');
set_rank(tags, 'likes_count');
tags.forEach( t => {
t.items_point = t.items_count * 100 / total_items_count;
t.followers_point = t.followers_count * 100 / total_followers_count;
t.likes_point = t.likes_count * 100 / total_likes_count;
t.score = (t.items_point * weight.item_count + t.followers_point * weight.followers_count + t.likes_point * weight.likes_count) / (weight.item_count + weight.followers_count + weight.likes_count);
t.score_old = (t.items_point * weight.item_count + t.followers_point * weight.followers_count) / (weight.item_count + weight.followers_count);
if (t.items_point > t.followers_point * 2.5 && t.items_point > t.likes_point * 2.5) {
t.type = 'ITEM+++';
t.color = 'rgba(0, 255, 0, 0.5)';
} else if (t.followers_point > t.items_point * 2.5 && t.followers_point > t.likes_point * 2.5) {
t.type = 'FOLLOWERS+++';
t.color = 'rgba(0, 0, 255, 0.5)';
} else if (t.likes_point > t.items_point * 2.5 && t.likes_point > t.followers_point * 2.5) {
t.type = 'LIKES+++';
t.color = 'rgba(255, 0, 0, 0.5)';
} else if (t.items_point * 2.5 < t.followers_point && t.items_point * 2.5 < t.likes_point) {
t.type = 'ITEM---';
t.color = 'rgba(255, 0, 255, 0.4)';
} else if (t.followers_point * 2.5 < t.items_point && t.followers_point * 2.5 < t.likes_point) {
t.type = 'FOLLOWERS---';
t.color = 'rgba(255, 255, 0, 0.4)';
} else if (t.likes_point * 2.5 < t.items_point && t.likes_point * 2.5 < t.followers_point) {
t.type = 'LIKES---';
t.color = 'rgba(0, 255, 255, 0.4)';
} else if (t.items_point > t.followers_point * 1.6 && t.items_point > t.likes_point * 1.6) {
t.type = 'ITEM++';
t.color = 'rgba(0, 191, 0, 0.4)';
} else if (t.followers_point > t.items_point * 1.6 && t.followers_point > t.likes_point * 1.6) {
t.type = 'FOLLOWERS++';
t.color = 'rgba(0, 0, 191, 0.4)';
} else if (t.likes_point > t.items_point * 1.6 && t.likes_point > t.followers_point * 1.6) {
t.type = 'LIKES++';
t.color = 'rgba(191, 0, 0, 0.4)';
} else if (t.items_point * 1.6 < t.followers_point && t.items_point * 1.6 < t.likes_point) {
t.type = 'ITEM--';
t.color = 'rgba(191, 0, 191, 0.3)';
} else if (t.followers_point * 1.6 < t.items_point && t.followers_point * 1.6 < t.likes_point) {
t.type = 'FOLLOWERS--';
t.color = 'rgba(191, 191, 0, 0.3)';
} else if (t.likes_point * 1.6 < t.items_point && t.likes_point * 1.6 < t.followers_point) {
t.type = 'LIKES--';
t.color = 'rgba(0, 191, 191, 0.3)';
} else if (t.items_point > t.followers_point * 1.1 && t.items_point > t.likes_point * 1.1) {
t.type = 'ITEM+';
t.color = 'rgba(0, 127, 0, 0.3)';
} else if (t.followers_point > t.items_point * 1.1 && t.followers_point > t.likes_point * 1.1) {
t.type = 'FOLLOWERS+';
t.color = 'rgba(0, 0, 127, 0.3)';
} else if (t.likes_point > t.items_point * 1.1 && t.likes_point > t.followers_point * 1.1) {
t.type = 'LIKES+';
t.color = 'rgba(127, 0, 0, 0.3)';
} else if (t.items_point * 1.1 < t.followers_point && t.items_point * 1.1 < t.likes_point) {
t.type = 'ITEM-';
t.color = 'rgba(127, 0, 127, 0.2)';
} else if (t.followers_point * 1.1 < t.items_point && t.followers_point * 1.1 < t.likes_point) {
t.type = 'FOLLOWERS-';
t.color = 'rgba(127, 127, 0, 0.2)';
} else if (t.likes_point * 1.1 < t.items_point && t.likes_point * 1.1 < t.followers_point) {
t.type = 'LIKES-';
t.color = 'rgba(0, 127, 127, 0.2)';
} else {
t.type = 'NORMAL';
t.color = 'rgba(0, 0, 0, 0.3)';
}
t.diff = t.followers_point - t.items_point;
})
set_rank(tags, 'score_old');
set_rank(tags, 'score', true);
return tags.slice(0, num);
}
const to_chart_data = v => {
return { label: v.id, data: [ { x: v.followers_count, y: v.items_count, r: 6 + (8 * v.likes_point) } ], backgroundColor: v.color }
};
fs.readFile('./tags.json', 'utf8', function (err, text) {
let tags = JSON.parse(text);
tags = get_ranked_json(tags, OUTPUT_RANKING_NUM, WEIGHT);
let str = '"タグ名",評価値順位,フォロワー数順位,記事数順位,いいね数順位,評価値,フォロワー数,記事数,いいね数,タイプ\n';
str += tags.map( v => `"${v.id}",${v.score_rank},${v.followers_count_rank},${v.items_count_rank},${v.likes_count_rank},${v.score},${v.followers_count},${v.items_count},${v.likes_count},${v.type}`).join('\n')
fs.writeFile(`list.csv`, str , (err, data) => {});
fs.writeFile(`chart.json`, JSON.stringify(tags.filter( v => v.score_rank <= OUTPUT_RANKING_NUM ).map(to_chart_data)) , (err, data) => {});
// 最後ソートしてから
tags.sort( (a, b) => b.score_old - a.score_old );
str = '"タグ名",評価値順位,フォロワー数順位,記事数順位,評価値,フォロワー数,記事数,評価値差分,\n';
str += tags.map( v => `"${v.id}",${v.score_old_rank},${v.followers_count_rank},${v.items_count_rank},${v.score_old},${v.followers_count},${v.items_count},${v.diff}`).join('\n')
fs.writeFile(`list_old.csv`, str , (err, data) => {});
});
さいごに
意外と感覚的に正しそうな値が出たので満足したが、2018/12~2019/3以外に、時系列で同様の分析をしても新たな発見があるかも。(リクエストがあれば。)
これで、関連記事に同じ観点の記事が出たら負け。