4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Notesは終わったのかQiitaAPIで記事数を取得してplotlyでグラフ化する

Posted at

#目的

十数年共に歩んできた、ユーザーが気軽に掲示板やワークフローのEUCを作れるグループウェア、Notes。
サイボウズやOffice365が台頭してきて乗り換え検討をしても「ウチには膨大な業務アプリやEUCがあるから…」と結局立ち消えになるのを何度も目にしてきました。でもついに乗り換えるらしいです。

私はといえばノースキル状態で無茶ぶりされた時の名残で独学なりにちょっとNotesをいじれてしまうのでNotesで困ったことがあると、わりとお声がけいただきます。しかし結局独学なのでまずはGoogle先生に教えを乞う…のですが、最近、調べても検索にヒットしなさすぎではないですか?

・・・これはいよいよNotes人口減ってるのでは?

ちょうどQiitaAPIを叩いてみたかったので、QiitaでNotesという単語を含む記事を投稿日時とセットで抜き出して、年推移を見てみたいと思います。

試行錯誤の様子も時系列に記述しておりますのでQiitaAPIとplotly初心者のご参考になれば。

#環境
Visual Studio Code 1.55.0
Node.js 15.12.0
npm 7.6.3
axios 0.21.1
csv 5.5.0
Python 3.8
plotly 4.14.3

#試行その1

app01.js
const axios = require("axios");

async function main() {
  let response = await axios.get(
    "https://qiita.com/api/v2/items?per_page=100&query="+encodeURIComponent("Notes")
  );

  for (let i = 0; i < response.data.length; i++) {

   console.log(response.data[i].created_at);
 }
}

main();

「Notes」という文字列をencodeURIComponentで扱えるようにして、APIで取得し、コンソールにはcreated_atのみを出力しています。

以下の記事を参考にさせていただきました。
初心者がQiitaのタグ情報を取得しTOP10を可視化し考察する。

#結果その1

2021-04-22T09:57:54+09:00
2021-04-22T07:24:12+09:00
2021-04-21T22:13:02+09:00
2021-04-21T22:04:22+09:00
2021-04-21T14:44:37+09:00
2021-04-21T10:41:14+09:00
2021-04-20T14:10:05+09:00
2021-04-20T11:13:32+09:00
2021-04-19T23:52:45+09:00
2021-04-17T09:23:47+09:00
2021-04-16T18:04:08+09:00
2021-04-15T16:10:00+09:00
2021-04-13T23:35:50+09:00
2021-04-13T23:24:13+09:00
2021-04-13T10:21:38+09:00
2021-04-13T02:49:04+09:00
2021-04-11T16:34:04+09:00
2021-04-09T18:04:25+09:00
2021-04-09T17:24:52+09:00
2021-04-09T16:38:31+09:00
2021-04-08T17:29:03+09:00
2021-04-06T16:40:48+09:00
2021-04-06T13:59:42+09:00
2021-04-06T00:40:49+09:00
2021-04-06T00:15:49+09:00
2021-04-05T04:19:00+09:00
2021-04-04T16:06:49+09:00
2021-04-04T05:19:18+09:00
2021-04-03T10:05:59+09:00
2021-04-02T08:21:48+09:00
2021-03-31T22:16:22+09:00
2021-03-31T15:36:40+09:00
2021-03-31T09:23:14+09:00
2021-03-30T23:19:04+09:00
2021-03-29T20:08:14+09:00
2021-03-29T16:37:28+09:00
2021-03-28T12:13:19+09:00
2021-03-27T17:43:25+09:00
2021-03-27T17:38:21+09:00
2021-03-25T18:34:20+09:00
2021-03-25T02:02:42+09:00
2021-03-24T17:31:29+09:00
2021-03-24T15:21:08+09:00
2021-03-22T14:00:19+09:00
2021-03-22T00:24:36+09:00
2021-03-21T16:58:10+09:00
2021-03-20T12:17:29+09:00
2021-03-16T23:18:38+09:00
2021-03-16T21:43:12+09:00
2021-03-16T16:09:59+09:00
2021-03-16T14:32:29+09:00
2021-03-16T01:24:34+09:00
2021-03-15T11:40:48+09:00
2021-03-14T20:31:40+09:00
2021-03-13T06:05:06+09:00
2021-03-12T20:31:04+09:00
2021-03-11T21:10:02+09:00
2021-03-11T17:44:13+09:00
2021-03-11T12:57:36+09:00
2021-03-11T08:47:25+09:00
2021-03-10T19:19:34+09:00
2021-03-10T18:02:47+09:00
2021-03-10T14:51:25+09:00
2021-03-09T19:38:57+09:00
2021-03-08T07:48:44+09:00
2021-03-08T06:57:12+09:00
2021-03-08T02:29:27+09:00
2021-03-07T15:30:00+09:00
2021-03-05T18:14:13+09:00
2021-03-05T17:24:02+09:00
2021-03-04T13:32:09+09:00
2021-03-04T03:16:27+09:00
2021-03-03T19:00:53+09:00
2021-03-02T11:26:55+09:00
2021-03-01T22:21:39+09:00
2021-03-01T21:01:56+09:00
2021-02-28T21:45:02+09:00
2021-02-28T18:56:59+09:00
2021-02-28T02:08:35+09:00
2021-02-27T08:56:07+09:00
2021-02-27T04:00:50+09:00
2021-02-26T22:43:37+09:00
2021-02-26T16:09:20+09:00
2021-02-26T12:06:35+09:00
2021-02-25T16:14:36+09:00
2021-02-24T12:56:30+09:00
2021-02-24T11:23:13+09:00
2021-02-24T10:41:37+09:00
2021-02-22T07:43:35+09:00
2021-02-21T17:24:43+09:00
2021-02-21T13:23:03+09:00
2021-02-19T21:39:28+09:00
2021-02-16T08:31:53+09:00
2021-02-16T03:30:20+09:00
2021-02-16T00:56:52+09:00
2021-02-15T17:20:01+09:00
2021-02-15T14:54:25+09:00
2021-02-13T14:01:55+09:00
2021-02-12T15:51:49+09:00
2021-02-12T12:34:11+09:00

#考察その1
お。意外と最新の日付ではないか。Notesめ、やりおるやりおる。

。。。んなわきゃーない。

参考になるコードを先に見つけてしまったのでつい更新日時のみを引っ張ってしまいましたが、まっとうにまずはブラウザに以下を投入してAPIの返りの全体を見てみましょう。

"https://qiita.com/api/v2/items?per_page=100&query=%22+encodeURIComponent(%22Notes%22)"

出力されてきた記事タイトルと眺めてみると以下のような感じ。

title: "Redmineの注記にテンプレートを入れる(手抜き版)",
title: "MonacaアプリとSquareのPOSレジアプリを連携させる(iOS編)",
title: "WebサイトでSquareのPOSレジアプリを使う(iOS編)",
title: "Hapi.jsでHappy Coding - Part1: ルーティングとSwaggerプラグイン",

うん。Notes関係ねぇな。

今度はAPIで返ってきたWEBページ全体に対して「Notes」で文字検索してみます。

/edit?issue[notes]="
"notes": "notes for the transaction"
"notes": "取引に関する説明書き"

なーるほーどーなー。
一般的な用語だとこうなってしまうのか。

というわけで次は本文ではなくタグに絞って検索し、一緒に記事タイトルも出力するようにしてみましょう。

#試行その2

使えそうなAPIを探します。

"https://qiita.com/api/v2/docs"

これ(↓)がよさそう。

GET /api/v2/tags/:tag_id/items
指定されたタグが付けられた記事一覧を、タグを付けた日時の降順で返します。

page
ページ番号 (1から100まで)
Example: 1
Type: string
Pattern: /^[0-9]+$/
per_page
1ページあたりに含まれる要素数 (1から100まで)
Example: 20
Type: string
Pattern: /^[0-9]+$/

GET /api/v2/tags/:tag_id/items?page=1&per_page=20 HTTP/1.1
Host: api.example.com

以下を変更します。
"https://qiita.com/api/v2/items?per_page=100&query="+encodeURIComponent("Notes")

"https://qiita.com/api/v2/tags/Domino/items?per_page=100"

タイトルも出力するようにします。
console.log(response.data[i].created_at) ;

結果がちょっとごちゃっとしてたので改行を追加します。
console.log("");

app02.js
const axios = require("axios");

async function main() {
  let response = await axios.get(
    "https://qiita.com/api/v2/tags/notes/items?per_page=100"
  );

  for (let i = 0; i < response.data.length; i++) {

   console.log(response.data[i].created_at) ;
   console.log(response.data[i].title);
   console.log("");
 }
}

main();

結果は以下。

2021-04-06T13:59:42+09:00
notes 入力した文言が含まれるフィールドを一覧表示

2020-12-26T00:12:13+09:00
雑なsharepoint運用基礎講座3 番外編 vs Notes

2020-11-12T18:10:45+09:00
IBMi環境でDomino11.0.1にアップグレードしてはまったこと

2020-10-29T16:53:16+09:00
CentOS7 にDomino11を入れてみた

2020-07-02T16:12:48+09:00
NOTESクライアント メール通知音の消し方

2020-06-30T19:18:38+09:00
リモートワーク通信節約の小ネタ

2020-03-13T15:47:54+09:00
Macのメモ(Notes.app)から全テキストを取り出す JXA

2020-02-11T11:59:05+09:00
Notesの文書リンクを取得するボタンを作成(簡単にSlackなどへ貼り付けられます)

2020-01-28T15:48:31+09:00
Gsuiteセミナー@六本木 セミナーメモ

2019-12-15T15:21:09+09:00
Notes/Dominoの役立つリンク集

2019-12-15T15:04:28+09:00
IBM Notes リンク生成あれこれ

2019-12-15T15:00:09+09:00
Notes/Dominoの役立つ@式

2019-08-09T07:12:13+09:00
OnTimeをいじってみた「週の初めはなん曜日?」

2019-07-24T18:59:07+09:00
DjangoからLouts Notesへデータの受け渡し

2018-12-08T01:08:02+09:00
IBM、Notes/Domino(他)を売却

2018-06-26T13:35:52+09:00
Atom利用中パッケージ

2018-04-11T16:55:59+09:00
IBM Notes にCSVを読み込ませる

2017-12-05T11:41:58+09:00
Watson Language Translator を Java で呼び出す。編集中の Notes 文書から使う。

2017-09-26T01:03:05+09:00
「Windows8以降での@Browserinfo("Platform")の返り値の問題」に対処する

2016-12-27T10:48:37+09:00
ファイル形式 Structured Text と Lotus 1-2-3 (.wk4) のデータ構造

2016-12-19T20:56:09+09:00
VBScriptでLotusScriptを利用する

2016-06-20T19:02:22+09:00
ノーツビューを GetDocumentByKey などで検索できないときの確認事項

2016-06-13T12:07:57+09:00
ノーツ(LotusScipt)で、任意のネットワーク共有フォルダ・ファイルを開く

2016-02-09T18:01:12+09:00
ノーツのビューで、値があるのに空白表示されたり、おかしな内容が表示される場合、$nというフィールドが出来てしまっているかも。

2016-01-15T15:45:50+09:00
Macのメモ(Notes)からEvernoteにエクスポートするAppleScript

#考察その2
今度はちゃんとNotes関連の記事っぽいですね。よしよし。

API良いですね。欲しいタイトルだけをシンプルに結果を出してくれるのでノイズも少なくて生産性高くできそうです。

あと面白そうなNotesの記事も見つけられました。

Notesの文書リンクを取得するボタンを作成(簡単にSlackなどへ貼り付けられます)
Notes/Dominoの役立つリンク集

#試行その3
では本題。日付だけ出力、年別にカウントします。

正直なところ出力結果が30行くらいでしたので、console結果をExcelにコピペしてしまおうかと思ったのですが、@ranchi1977さんがcsv出力までされていたので負けじとトライしてみます。

JavaScriptとQiitaAPIで取得した人気タグ情報をplotlyで散布図表示してみる

モジュールをインストールします。
npm install csv

パッケージ行を追加します。
const fs = require("fs");
const csvStr = require("csv-stringify/lib/sync");
const csvParse = require('csv-parse/lib/sync');

せっかくCSVに出すのに1行だと寂しいので記事タイトルとURLも1行に出せるようにします。

app03.js
const axios = require("axios");
const fs = require("fs");
const csvStr = require("csv-stringify/lib/sync");
const csvParse = require('csv-parse/lib/sync');

async function main() {

    //csvに変換する用list
    let outcsv = [];

    //csvのヘッダー設定
    let columns = ["作成日","記事タイトル","URL"];
    outcsv.push(columns);

  let response = await axios.get(
    "https://qiita.com/api/v2/tags/notes/items?per_page=100"
  );

  for (let i = 0; i < response.data.length; i++) {
         //listに格納
         let record = [];
         record.push(response.data[i].created_at,response.data[i].title,response.data[i].url); 
         outcsv.push(record);
 }

     //   csvとして出力
     fs.writeFileSync("./qiita_list_notes.csv", csvStr(outcsv));

}

main()

#結果その3
無事、CSVに出力できました!

ただそのままExcelで開くと文字化けしており、その影響で列も崩れてしまっていました。

0425-mozibake.PNG

メモ帳で開くと「Unix UTF-8」との表記だったので、Excelで「Unicode(UTF-8)」を選択してインポートして、無事成功です。

0425-excel.PNG

#試行その4

このままプログラムでグラフにまでしてしまいたいものですがさてどうしたものか。

先に参考にさせていただいた記事の方々は散布図にPlotyを使ってらっしゃいましたが、これは棒グラフもできるものなのかしら。

[Python] Plotlyでぐりぐり動かせるグラフを作る

余裕で出来そうですね。がんばってみましょう。
(それにしてもぐりぐり動かせるグラフ作れるPlotly、すごい…)

さて、調査タイムです。そもそもJupyter Notebookってなんですか?

【初心者向け】Jupyter Notebookの使い方!インストール方法から解説

「Jupyter Notebook」は、PythonなどをWebブラウザ上で記述・実行できる統合開発環境です。
Jupyter Notebookは、統計のモデリングや機械学習などデータ分析に使用されることが想定されており、データの視覚化などの作業に適しています。対話型の開発環境であるため、前の実行結果に応じて、次に実行するプログラムや作業を選択できます。なお、実行した結果は作業履歴として記録に残ります。
また、オープンソースで提供されているため、無料で利用が可能です。コミュニティによる機能のアップデートも頻繁に行われています。

以下のモジュールをインストールしてみました。
Anaconda3-2020.11-Windows-x86_64

Jupyter Notebookでは、赤枠のセルと呼ばれる部分にソースコードを入力していきます。「実行」ボタンをクリックするだけでソース―コードの実行結果を確認できるため、記述内容を確認しながらプログラムを作成できます。

Pythonでグラフを作成するためには、「matplotlib」というライブラリを使用するのが一般的です。

棒グラフを書きたい場合は、「numpy」ライブラリを使用します。

作成したPythonコードやマークダウンのファイルは、共有することが可能です。共有形式は、Jupyter Notebook自体のデータと、PDF、HTML、Pythonなどの形式が選べます。コードを共有して複数人で修正やカスタマイズする場合や、マークダウンやグラフなどの結果のみをクライアントなどに確認してもらう際に便利です。Jupyter Notebookを使用している人に向け、コードを含めたJupyter Notebookデータを共有したい場合は、「.ipynb」ファイルとして保存し、共有します。

さて、見様見真似で実行です。
あれ、CSVの場所を指定したいのですがJupyter Notebookでディレクトリ移動はどうするのかしら。。

Jupyter Notebookでのディレクトリ操作

カレントディレクトリの取得は%pwdで行えます。
ファイルリストの取得(表示)は%lsコマンドで行えます。
カレントディレクトリの移動は%cdコマンドで行えます。
%cd /anaconda3 # /anaconda3ディレクトに移動
%cd        # ホームディレクトリに移動

ふむふむ。Jupyter Notebookでディレクトリを移動していから以下のコードを投入していきます。

pip install plotly
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from plotly.offline import init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
df = pd.read_csv("./qiita_list_notes.csv")
df.head(20)

とりあえず20行取り出すことには成功しました。

0425-jupyter01.PNG

ここからグラフにしていきます。
年単位の合計値の棒グラフか、日付プロットの点で密度を見せるか、どちらかをしたいところですが果たしてPlotlyでどうやればいいものやら調査します。

Plotlyでレポート・論文に使えるグラフを描こう

綺麗なグラフのサンプルコードがいっぱいです。(↓)そのうち使いこなしたい。
Plotly Python Open Source Graphing Library

そもそも基礎がわかってないのでそのあたりを調査します。

Pandas DataFrameを徹底解説!(作成、行・列の追加と削除、indexなど)

Pandas(パンダス)とは、データを効率的に扱うために開発されたPythonのライブラリの1つで、データの取り込みや加工・集計、分析処理に利用します。
Pandasには2つの主要なデータ構造があり、Series(シリーズ)が1次元のデータ、DataFrame(データフレーム)が2次元のデータに対応します。

Pandas Excel、CSVファイルの読み込み、書き込み(出力)

作業を行っているディレクトリではなく、別のディレクトリからファイルを読み込む場合、ディレクトリの指定方法は、ファイル名の前にディレクトリを記述します。その際に、その前にrを付加します。例えば、ディレクトリ”C:\Test_Folder\Test_Folder2\Test_Folder3”の配下に先ほど利用したCSVファイル”T_Sales_Header.csv”を保存し、このCSVファイルを読み込む場合は、次のように書きます。
In [2]: df_sales = pd.read_csv(r"C:\Test_Folder\Test_Folder2\Test_Folder3\T_Sales_Header.csv"
...: , index_col=["Sales_No"])
...: df_sales.head()

Pandas 時系列データの集計(年度/月ごとに集計、resampleの使い方、移動平均など)

DataFrameにおいて時系列のデータをある期間でグルーピングし直す場合、resampleを使います。
DataFrame.resample(rule = 期間)
DataFrameには時系列のデータを格納します。また引数ruleではグルーピングし直す期間を指定します。期間は次のように記号で指定します。

記号 期間
A 年
M 月
Q 四半期
W 週

このようにresampleを利用して時系列のデータをグルーピングし直し、その結果を元に平均、最大値などを求めていきます。例えば平均を求める場合、以下のようにresampleの後に.mean()を付けて平均を求めます。

DataFrame.resample(rule = 期間).mean()

pandasで時系列データをリサンプリングするresample, asfreq

resample()の第一引数ruleにも、asfreq()と同様にD(日次)、W(週次)などの頻度コードを指定する。詳細は以下の記事を参照。
resample()が返すのはDatetimeIndexResampler型のオブジェクトで、それ自体をprint()で出力しても値は表示されない。
mean()(平均値)やmedian()(中央値)、sum()(合計)などのメソッドを呼ぶことで集約された値が算出される。
ほかにも、先頭の値、末尾の値を出力するfirst(), last()、個数を出力するcount()、OHLC(Open: 始値、High: 高値、Low: 安値、Close: 終値)を出力するohlc()もある。
print(df.resample('W').count())

やっと材料が揃った。。。resampleでcountを使えばいけるのではなかろうか。

df = pd.read_csv("./qiita_list_notes.csv", index_col=["作成日"])
df.resample(rule = "A").count()

0425-1744-error.PNG

いけなかった。

んー。TypeErrorというからには型が違うときのエラーのようだ。
そういえば「作成日」列が日付であると宣言してないような?

pandasのindexはdatetimeにすると便利

df["date"] = pd.to_datetime(df["date"])
df = df.set_index("date")
df.head()

Pandasで時間や日付データに変換するto_datetime関数の使い方

この関数はある程度ならフォーマットを検知してくれるのでとりあえず入れてみてうまく行かなかったらフォーマットを個別に指定するという形で使っても問題ありません。
str_1 = '2019/04/07'
pd.to_datetime(str_1)
ですが、このような例の場合、フォーマットを関数が内部で判断する必要があるため、大規模なデータを一括でdatetime64型に変換しようとすると処理の時間にかなりの差が出てきますのでデータ数が多いほどフォーマットを指定することをオススメします。
フォーマットの指定の仕方ですが、format引数で指定できます。
先ほどの"2019/04/07"ですと、format='%Y/%m/%d'のように指定します。ここの'%Y'は4桁の年数となり、小文字の'%y'は2桁の年数となります。
time_sr = pd.Series([20181024, 20200915, 20210111])
convert_time = pd.to_datetime(time_sr, format='%Y%m%d')
convert_time

よし、これででき・・・ない。
0425-1847-error.PNG

んー。あれ、そういえばCSV取り込みのときに主キー設定してたな。

df = pd.read_csv("./qiita_list_notes.csv", index_col=["作成日"])

これ外してみよう。

df = pd.read_csv("./qiita_list_notes.csv")
df["作成日"] = pd.to_datetime(df["作成日"])
df = df.set_index("作成日")
df.resample(rule = "A").count()

0425-1850-success.PNG

できました!
やー、これはうれしい。ひさびさにガチャガチャと試行錯誤しました。

あとはこれを棒グラフにしましょう。

0425-1934-error.PNG

はい、できませーん。

なんでや!

んー、X軸が「作成日」かと言われるとcountしてるから微妙だな。
これX軸の指定削って、自動の処理に任せたらどうなるのかな。

df = pd.read_csv("./qiita_list_notes.csv")
df["作成日"] = pd.to_datetime(df["作成日"])
df = df.set_index("作成日")
df2 = df.resample(rule = "A").count()
fig = px.bar(df2, y="記事タイトル")
fig.show()

0425-1938-success.PNG

できた!

#考察
件数が少ないのがネックですが、グラフ結果としては右肩上がりであり、Notesは今後一層の盛り上がりが予想されます(?)

正直予想外でした。これがGoogle検索数や教えてgoo、Yahoo知恵袋の質問数であれば「玄人エンジニアが減って、無茶ぶりされて困った人が検索して件数増えたのかな」と思えるのですがQiita記事ですからね。ちょっと原因がわからない。

#今後に向けて
欲を言うとグラフの「作成日」を「作成年」に、「記事タイトル」を「記事数」に変更したかったのですが時間切れ。簡単そうに思ったのですが、参考コードの継ぎ接ぎコピペではうまくいかず。

X軸とY軸の名前をlayoutに定義して、barのlayout引数に入れるだけでよさそうだったんですが、うーん。go.Figure()との違いとかがわかっていないので、基礎から固めたほうが良さそう。

Plotly 基本チャート - バーチャート・棒グラフ(1)

あと表とグラフを見比べるとグラフが1年先に進んでます。グラフ化するときの仕様? countした表の日付を見るとxxxx年12月31日なので日本時間とグリニッジ標準時との差で次の年に計上されてたりする?

#参考URL
調べたもののまだ読めてないのであとで読むメモ。

DatetimeIndexがわからんので逆引きでまとめとくの巻
Python pandas で日時関連のデータ操作をカンタンに
時系列データを読み込み,DatetimeIndexを持つpandas.DataFrameを作る
【Tips編】集計からダッシュボードの作成まで1本化!PythonとDashによるデータ可視化アプリ開発 〜様々なグラフを作成する〜

プログラムでグラフ化できなければExcelのピポッドでやろうとしたときのメモ。
これはこれで使いこなせるようになっておきたい。

Excelで年/四半期/月/週あたりの発生数をカウントする方法は?

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?