Chart JSを利用してグラフ化してみよう
最後に、ツイッターのいいね数とリツイート数をグラフ化しましょう。
②いいね数とリツイート数のグラフを作成しよう
さて、最後に画像の②にあたる部分を作成します。 ここの部分をもう少し細かく見てみましょう。 リツイート数と、いいね数を一日ごとに計算しています。
今回は、chartjsという、JavaScriptのライブラリを利用して、グラフ表示を行っていきたいと思います。
ChartJSのインポート
ChartJSは、JavaScriptで利用できる、グラフ表示のライブラリです。
CDNでインポートします。まず、利用するためにheadタグ内にChartJSのCDNをコピーしておいてください。
https://www.chartjs.org/docs/latest/
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js">
Canvasタグの準備
次に、HTMLのここの部分の実装を見ていきましょう。
<!-- chart -->
<div class="row">
<div class="col-md-12">
<canvas id="chart"></canvas>
</div>
</div>
実装が必要なのは、canvasタグにidを入れた1行のみです。
のちほど、ここに対してJavaScriptで処理した値を、canvas上に出力する形になります。
グラフの実装
そこの部分は、bodyタグの下の部分で実装されているので、見てみましょう。ちょっと長いですが、最終的には以下のようなコードになります。
<script>
{% if profile %}
// bar chart data
var barData = {
labels : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
"{{row['created_at']}}",
{% endfor %}],
datasets : [
{
label: "Retweets",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth:10,
bezierCurve : false,
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["retweets"]}},
{% endfor %}]
},{
label: "Favorites",
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["fav"]}},
{% endfor %}],
type: 'line',
borderColor: 'rgb(63, 127, 191)',
}
]
}
// draw bar chart
var mychart = document.getElementById("chart");
var chart = new Chart(mychart, {
type:'bar',
data:barData,
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
min: 0,
max: 1000
}
}
]
}
}
});
{% endif %}
</script>
これを理解するために、まずはChartJSの仕様について理解していきましょう。
全体としては、
- グラフのタイプ(棒グラフetc)の設定
- 挿入するデータの設定
- オプションの設定
となります。コードにすると、以下の通りです。
var ctx = document.getElementById("myChart"); //canvas要素の取得
var myChart = new Chart(ctx, {
type: タイプ, //描画するグラフの種類
data: データ, //ラベルとデータセット
options: オプション //オプション設定
});
type:グラフの種類
リファレンスに詳しく書いてありますが、グラフのtypeには、以下のような値を入れることができます。
https://www.chartjs.org/docs/latest/charts/
bar | 棒グラフ |
line | 折れ線グラフ |
rader | レーダーチャート |
今回利用するのは棒グラフのため、barを利用します。
グラフに表示するデータ data
次に、グラフに表示するデータについての設定を行います。
dataには、大きく分けて
- label:X軸のデータ。今回の場合だと日付にあたります。
- datasets:Y軸のデータ。今回の場合だといいね数、リツイート数にあたります。
があります。
labelは、X軸のデータです。以下のコードのように、X軸に出力する値をリスト形式でいれていきます。
labels : ["1/1", "1/2", "1/3", "1/4"] //X軸に出力する値をリスト形式で代入する
created_atで日付のデータを取得できているので、tweetsにたいしてfor文で、一つ一つcreated_atを出力し、配列として挿入します。
FlaskのHTML内でfor文を利用する場合には、{% for %} {% endfor %}で囲ってあげる必要があります。
実際に実装する場合は、以下のようなコードになります。
var barData = {
labels : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
"{{row['created_at']}}",
{% endfor %}],
datasets
datasetsは、グラフ上で表示するデータを扱います。今回は、datasetが以下の2つあります。
- いいね
- リツイート
を扱います。それぞれ別のデータなので、datasetsに配列として入れます。
棒グラフのdatasetとして利用できる値としては、以下のドキュメントに詳しく記載されています。
https://www.chartjs.org/docs/latest/charts/bar.html
Name | Type | Scriptable | Indexable | Default |
---|---|---|---|---|
backgroundColor |
Color |
Yes | Yes | 'rgba(0, 0, 0, 0.1)' |
borderColor |
Color |
Yes | Yes | 'rgba(0, 0, 0, 0.1)' |
borderSkipped |
string |
Yes | Yes | 'bottom' |
borderWidth |
number|object |
Yes | Yes | 0 |
data |
object[] |
- | - | required |
hoverBackgroundColor |
Color |
- | Yes | undefined |
hoverBorderColor |
Color |
- | Yes | undefined |
hoverBorderWidth |
number |
- | Yes | 1 |
label |
string |
- | - | '' |
order |
number |
- | - | 0 |
xAxisID |
string |
- | - | first x axis |
yAxisID |
string |
- | - | first y axis |
datasets : [
{
label: "Retweets", //ラベル名
backgroundColor: 'rgba(255, 99, 132, 0.2)', //背景色
borderColor: 'rgba(255,99,132,1)', //枠線の色
borderWidth:10, //枠線の太さ
bezierCurve : false,
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["retweets"]}},
{% endfor %}]
},{
label: "Favorites",
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["fav"]}},
{% endfor %}],
type: 'line',
borderColor: 'rgb(63, 127, 191)',
}
]
}
で、グラフ描画の部分がこちらですね。
// draw bar chart
var mychart = document.getElementById("chart");
var chart = new Chart(mychart, {
type:'bar',
data:barData,
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
min: 0,
max: 1000
}
}
]
}
}
});
縦軸の上限を1000に設定したいです。というのも、ユーザーを単純に比較したいので。その場合はoptionsで設定してあげます。
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
min: 0,
max: 1000
}
}
]
}
}
こんな感じでやると、グラフが描画できるはずです。
再度flaskを起動して、特定のアカウントのIDを入れると完成です。
最終的なコードをこちらにまとめておきます。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toptweets</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>;;
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>;;
<![endif]-->
</head>
<body class="bg-gray">
<main>
<div class="container">
<!-- 検索フォーム -->
<div class="row">
<div class="col-md-12 p-2">
<h1><a href="/" class="no-decoration">Toptweets</a></h1>
<p>ツイッターのIDを入れると、直近のいいね数とリツイート数、最もリツイートされたツイートが一覧できます。</p>
<form class="form-inline" method="post">
<div class="form-group">
<label class="sr-only" for="screen_name"></label>
<div class="input-group mb-3">
<span class="input-group-text">@</span>
<input id="screen_name" name="screen_name" placeholder="ここにツイッターのIDを入力してください。" type="text" class="form-control">
</div>
<button type="submit" class="btn btn-primary">取得する</button>
</div>
</form>
</div><!-- col-md-12 -->
</div><!-- row -->
{% if profile %}
<!-- chart -->
<div class="row mb-3">
<div class="col-md-12">
<h2 class="mb-3">{{profile.screen_name}}さんの最近の投稿パフォーマンス</h2>
<canvas id="chart"></canvas>
</div>
</div><!-- row -->
{% endif %}
{% if profile %}
<div class="row">
<!-- プロフィール -->
<div class="col-md-4">
<div class="bg-white p-5">
<div class="pb-3 border-bottom">
<img src="{{profile.image}}" width="100px">
<a href="https://twitter.com/{{profile.screen_name}}">@{{profile.screen_name}}</a>
<div class="pt-3">{{profile.description}}</div>
</div>
<div class="pt-3">
{% for index, row in grouped_df.iterrows() %}
<div>{{index}}
<span class="float-end">
<i class="fas fa-retweet" style="color: limegreen;"></i>{{row["retweets"]}}
<i class="fa fa-heart pr-2" style="color: hotpink;"></i> {{row["fav"]}}
</span>
</div>
{% endfor %}
</div>
</div>
</div><!-- col-md-4 -->
<!-- ツイート一覧 -->
<div class="col-md-8">
<h2 class="mb-3">もっともリツイート・いいねされたツイート</h2>
{% for index, row in sorted_df.iterrows() %}
<div class="bg-white p-3 border-bottom mb-3">
<div>{{row["created_at"]}}</div>
<div class="tweet-text">{{row["text"]}}</div>
<div class="">
<i class="fas fa-retweet" style="color: limegreen;"></i><span class="pr-2">{{ row["retweets"] }}</span>
<i class="fa fa-heart" style="color: hotpink;"></i><span class="pr-2">{{ row["fav"] }}</span>
</div>
</div>
{% endfor %}
{% endif %}
</div><!-- col-md-8 -->
</div><!-- row -->
</div><!-- container -->
<! -- ここがBootstrapのCDN -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<!-- chartjs -->
<script>
{% if profile %}
// bar chart data
var barData = {
labels : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
"{{row['created_at']}}",
{% endfor %}],
datasets : [
{
label: "リツイート数",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth:10,
bezierCurve : false,
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["retweets"]}},
{% endfor %}]
},{
label: "いいね数",
data : [{% for index, row in tweets_df.sort_values(by="created_at", ascending=True).iterrows() %}
{{row["fav"]}},
{% endfor %}],
type: 'line',
borderColor: 'rgb(63, 127, 191)',
}
]
}
// draw bar chart
var mychart = document.getElementById("chart");
var chart = new Chart(mychart, {
type:'bar',
data:barData,
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
min: 0,
max: 1000
}
}
]
}
}
});
{% endif %}
</script>
</main>
</body>
</html>
app.py
from flask import Flask, render_template, request
import os
from dotenv import load_dotenv
import tweepy
import pandas as pd
app = Flask(__name__)
load_dotenv()
CONSUMER_KEY = os.getenv("CONSUMER_KEY")
CONSUMER_SECRET = os.getenv("CONSUMER_SECRET")
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
ACCESS_SECRET = os.getenv("ACCESS_SECRET")
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
api = tweepy.API(auth)
@app.route('/', methods = ["GET" , "POST"])
def index():
if request.method == 'POST':
screen_name = request.form['screen_name']
tweets_df = get_tweets_df(screen_name)
return render_template(
'index.html',
profile=get_profile(screen_name),
tweets_df = tweets_df,
grouped_df = get_grouped_df(tweets_df),
sorted_df = get_sorted_df(tweets_df)
)
return render_template('index.html')
def get_tweets_df(screen_name):
columns = [
"tweet_id",
"created_at",
"text",
"fav",
"retweets"
]
tweets_df = pd.DataFrame(columns=columns) #1
for tweet in tweepy.Cursor(api.user_timeline,screen_name = screen_name, exclude_replies = True).items(): #2
try:
if not "RT @" in tweet.text: #3
se = pd.Series([ #4
tweet.id,
tweet.created_at,
tweet.text.replace('\n',''),
tweet.favorite_count,
tweet.retweet_count
]
,columns
)
tweets_df = tweets_df.append(se,ignore_index=True) #5
except Exception as e:
print (e)
tweets_df["created_at"] = pd.to_datetime(tweets_df["created_at"]) #6
return tweets_df
def get_profile(screen_name):
user = api.get_user(screen_name= screen_name)
profile = {
"id": user.id,
"screen_name": screen_name,
"image": user.profile_image_url,
"description": user.description
}
return profile
def get_grouped_df(tweets_df):
grouped_df = tweets_df.groupby([tweets_df["created_at"].dt.date]).sum().sort_values(by="created_at", ascending=False)
return grouped_df
def get_sorted_df(tweets_df):
sorted_df = tweets_df.sort_values(by="retweets", ascending=False)
return sorted_df
if __name__ == '__main__':
app.run(host = '0.0.0.0', port = 50000, debug=True)
style.css
.bg-gray{
background-color:#e6ecf0;
}
.bg-white{
background-color:#ffffff;
}
.no-decoration{
text-decoration: none;
}
.env
CONSUMER_KEY=xxxxx
CONSUMER_SECRET=xxxxx
ACCESS_TOKEN=xxxxx
ACCESS_SECRET=xxxxx
このチュートリアルはこれで終了です。