概要
QiitaとZennってどんな特徴があるの?
前回Zennのスクレイピングを投稿したのですが、
そちらで取得した情報をPythonを用いて時系列順に分析してみました。
目次
1.タグ情報の時系列グラフ
2.実行環境
3.コード概要
4.感想
5.最後に
1.タグ情報の時系列グラフ
結果はこのようなグラフが得られました。
タグ情報の全体に占める割合の移動平均をとったグラフになります。
2.実行環境
今回、Google Colaboratoryで実施しました。
3.コード概要
前回のスクレイピングで作成したcsvファイルを読み込みます。
まずはgoogle driveとの連携を作成します。
from google.colab import drive
drive.mount('/content/drive')
csvファイルを読み込みます。
pandasを利用して読み込みます。
import pandas as pd
#fileNoを定義
target_read_file = '/content/drive/MyDrive/csv/Zenn_allinfo_20220210_all.csv'
#csvからページ名を取得
df = pd.read_csv(target_read_file)
#データを確認
df.head(1)
下記の様なデータフレームが取得できます。
title | author | link | time | tag_1 | tag_2 | tag_3 | tag_4 | tag_5 | tag_6 | tag_7 | tag_8 | tag_9 | tag_10 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
分析するにあたり、tag情報が1~10まで別のカラムに格納されているのが、都合が悪いので、
一つのtagというカラムにまとめます。
# 変数d_listに空のリストを作成する
d_list = []
#処理数nを定義
n=0
for time,tag_1,tag_2,tag_3,tag_4,tag_5,tag_6,tag_7,tag_8,tag_9,tag_10 in zip(df.time,df.tag_1,df.tag_2,df.tag_3,df.tag_4,df.tag_5,df.tag_6,df.tag_7,df.tag_8,df.tag_9,df.tag_10):
d1 = {'time':time,
'tag': tag_1}
d_list.append(d1)
d2 = {'time':time,
'tag': tag_2}
d_list.append(d2)
d3 = {'time':time,
'tag': tag_3}
d_list.append(d3)
d4 = {'time':time,
'tag': tag_4}
d_list.append(d4)
d5 = {'time':time,
'tag': tag_5}
d_list.append(d5)
d6 = {'time':time,
'tag': tag_6}
d_list.append(d6)
d7 = {'time':time,
'tag': tag_7}
d_list.append(d7)
d8 = {'time':time,
'tag': tag_8}
d_list.append(d8)
d9 = {'time':time,
'tag': tag_9}
d_list.append(d9)
d10 = {'time':time,
'tag': tag_10}
d_list.append(d10)
# 変数d_listを使って、データフレームを作成する
df = pd.DataFrame(d_list)
df.head(10)
time | tag | |
---|---|---|
0 | 2022-02-14T00:42:44+00:00 | Firebase |
1 | 2022-02-14T00:42:44+00:00 | TypeScript |
2 | 2022-02-14T00:42:44+00:00 | frourio |
3 | 2022-02-14T00:42:44+00:00 | railway |
4 | 2022-02-14T00:42:44+00:00 | tech |
5 | 2022-02-14T00:42:44+00:00 | NaN |
6 | 2022-02-14T00:42:44+00:00 | NaN |
7 | 2022-02-14T00:42:44+00:00 | NaN |
8 | 2022-02-14T00:42:44+00:00 | NaN |
9 | 2022-02-14T00:42:44+00:00 | NaN |
Null値を削除します。
df2 = df[df['tag'].notnull()]
df2.head(10)
time | tag | |
---|---|---|
0 | 2022-02-14T00:42:44+00:00 | Firebase |
1 | 2022-02-14T00:42:44+00:00 | TypeScript |
2 | 2022-02-14T00:42:44+00:00 | frourio |
3 | 2022-02-14T00:42:44+00:00 | railway |
4 | 2022-02-14T00:42:44+00:00 | tech |
10 | 2022-02-14T00:42:24+00:00 | JavaScript |
11 | 2022-02-14T00:42:24+00:00 | React |
12 | 2022-02-14T00:42:24+00:00 | Vue.js |
13 | 2022-02-14T00:42:24+00:00 | SWR |
14 | 2022-02-14T00:42:24+00:00 | tech |
Null値が消えました。
続いてどういったタグがあるか確認します。
df2['tag'].value_counts()
tech 23387
idea 3547
JavaScript 1893
TypeScript 1671
Python 1656
...
研鑽 1
セルフマネジメント 1
webex 1
回帰 1
githubprofile 1
Name: tag, Length: 9000, dtype: int64
確認すると、「tech」が圧倒的に多く次いで、「idea」というタグが多いという結果です。
これはZennの仕様上、techかideaのどちらかのタグをつけるためなので、
これら二つのタグは省いて処理していきます。
またそのままですと日本語表示がされないので、
「japanize-matplotlib」というライブラリをインポートして活用します。
!pip install japanize-matplotlib
import japanize_matplotlib
import matplotlib.pyplot as plt
df2['tag'].value_counts()[2:22].plot.bar()
plt.title('ランキングALL')
value_counts()[2:22]とすることで、3位から22位までの20ランキングを取得します。
次にタグ情報の移り変わりを時系列で整理します。
月単位で集計しようと思うので、月単位の日時情報でまとめます。
df2['date']=pd.to_datetime(df2['time']).dt.strftime('%Y-%m')
df2.head()
time | tag | date | |
---|---|---|---|
0 | 2022-02-14T00:42:44+00:00 | Firebase | 2022-02 |
1 | 2022-02-14T00:42:44+00:00 | TypeScript | 2022-02 |
2 | 2022-02-14T00:42:44+00:00 | frourio | 2022-02 |
3 | 2022-02-14T00:42:44+00:00 | railway | 2022-02 |
4 | 2022-02-14T00:42:44+00:00 | tech | 2022-02 |
タグの数は月毎に総数が異なるため、3か月毎に移動平均をとります。
まず合計タグ数の移動平均を作成し、タグ数/合計タグ数を行うことでデータを偏りを減らします。
まず合計の移動平均を算出して、データフレームを作成し、
mergeで合算して必要なデータフレームを作成します。
df_sum=df2[['date','tag']].groupby(['date']).count()
df_sum.columns=['sum']
#移動平均
w_size=3
v = np.ones(w_size) / w_size
# a=df_sum['sum']
out = df_sum.apply(lambda x: np.convolve(x, v, mode='same'),axis=0)
out.columns=['sum_m_ave']
b=pd.DataFrame(out,columns=['sum_m_ave'])
df_sum2 = df_sum.merge(b,left_index=True, right_index=True, how='left')
できあがったセルはこのような形になります。
print(df_sum2)
date | sum | sum_ave |
---|---|---|
2020-09 | 3585 | 2858.333333 |
2020-10 | 4990 | 4145.000000 |
2020-11 | 3860 | 5095.666667 |
2020-12 | 6437 | 5245.000000 |
2021-01 | 5438 | 5679.333333 |
2021-02 | 5163 | 5495.333333 |
2021-03 | 5885 | 5468.666667 |
2021-04 | 5358 | 5806.000000 |
2021-05 | 6175 | 5589.000000 |
2021-06 | 5234 | 5617.333333 |
2021-07 | 5443 | 5324.000000 |
2021-08 | 5295 | 5483.000000 |
2021-09 | 5711 | 5351.000000 |
2021-10 | 5047 | 5270.666667 |
2021-11 | 5054 | 6587.666667 |
2021-12 | 9662 | 7275.333333 |
2022-01 | 7110 | 6783.666667 |
2022-02 | 3579 | 3563.000000 |
続いて個別のタグ情報も移動平均を算出し、
辞書型でデータを格納します。
result = {}
for i, t in enumerate(df2['tag'].value_counts().index):
#tag情報と同一のデータのみで集計
df_tmp = df2[df2['tag']==t]
df_gb = df_tmp.groupby('date').count()
df_gb = df_gb.drop('time',axis=1)
#移動平均算出
w_size=3
v = np.ones(w_size) / w_size
out = df_gb.apply(lambda x: np.convolve(x, v, mode='same'),axis=0)
out.columns=['m_ave']
df_m = df_gb.merge(out,left_index=True, right_index=True, how='left')
df_m = df_m.merge(df_sum2,left_index=True, right_index=True, how='left')
#割合算出
df_m['ratio'] = df_m['tag'] / df_m['sum']
df_m['ratio_m_ave'] = df_m['m_ave'] / df_m['sum_m_ave']
#結果を格納
r={'rank':i+1, 'df':df_m}
result[t] = r
この結果をグラフ化します。
まずは単純な割合でグラフ化します。
グラフ作成時、横軸がかぶってしまって見えなくなってしまったので、
下記ページを参考にしました。
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
plt.ylabel('ratio',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i = 1
for k, v in result.items():
rank = v['rank']
df_tmp = v['df']
if rank < 3:
continue
if rank > 22:
continue
plt.plot(df_tmp['ratio'],label=k)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)
plt.show()
グラフの凡例数が多くて見づらいので、
凡例数を10個ずつにして、再度出力します。
plt.figure(figsize=(10, 10))
plt.ylabel('ratio',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i = 1
for k, v in result.items():
# plt.clf()
rank = v['rank']
df_tmp = v['df']
if rank < 3:
continue
if rank > 22:
break
plt.plot(df_tmp['ratio'],label=k)
if i % 10 == 0:
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)
plt.show()
plt.clf()
if i // 10 < 2:
plt.figure(figsize=(10, 10))
plt.ylabel('ratio',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i += 1
つづいて、移動平均での割合グラフを出力します。
全体グラフ出力
plt.figure(figsize=(10, 10))
plt.ylabel('ratio_m_ave',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i = 1
for k, v in result.items():
rank = v['rank']
df_tmp = v['df']
if rank < 3:
continue
if rank > 22:
continue
plt.plot(df_tmp['ratio_m_ave'],label=k)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)
plt.show()
個別グラフ出力
plt.figure(figsize=(10, 10))
plt.ylabel('ratio_m_ave',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i = 1
for k, v in result.items():
# plt.clf()
rank = v['rank']
df_tmp = v['df']
if rank < 3:
continue
if rank > 22:
continue
plt.plot(df_tmp['ratio_m_ave'],label=k)
if i % 10 == 0:
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)
plt.show()
plt.clf()
if i // 10 < 2:
plt.figure(figsize=(10, 10))
plt.ylabel('ratio_m_ave',size=16)
#x軸ラベルを90度回転
plt.xticks(rotation=90)
i += 1
4.感想
移動平均にしてみると、上位層(TOP5)が安定していることがわかりました。
また、初めは初心者が2位だったにも関わらず、順位をかなり落としており、
玄人感が特徴として出ているのかなと感じました。
5.最後に
タグ情報をグラフかして見える化してみましたが、
視覚化すると見えてきたり、移動平均をとってみると、
違った見方ができて面白いなと思いました。
まだまだ、難しいですがそういったことが、
プログラム上でできてしまう、pythonは改めて便利だなとも感じました。