Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add TrendTag function and API #109

Merged
merged 3 commits into from
Nov 16, 2017
Merged

add TrendTag function and API #109

merged 3 commits into from
Nov 16, 2017

Conversation

fvh-P
Copy link
Collaborator

@fvh-P fvh-P commented Nov 2, 2017

Related #85.

Add a function aggregating trend tags using statuses in the past 30 minutes, and an API showing trend tags.

過去30分間の投稿からトレンドタグを集計する機能、および集計結果を表示するAPIを追加します。

集計について

集計は10分ごとに行います。
10分前から現在までに用いられたハッシュタグとその使用回数を取得し、Redisに格納してある1期前(10~20分前)と2期前(20~30分前)の使用回数データも用いて集計します。

トレンドスコアの算出は以下の計算式で行います。
当期の使用回数をn、1期前の使用回数をl、2期前の使用回数をbとすると
n + (n - l) + (l * 3 / 4.0) + ((l - b) / 2.0) + (b / 4.0)
つまり、

2期前 1期前 当期
使用回数 ×0.25 ×0.75 ×1
前期からの増分 -- ×0.5 ×1
スコア -- -- X

この表の各倍率をかけて足した値がXになります。

具体例)
タグA

0:00 0:10 0:20 0:30 0:40 0:50 1:00 1:10 1:20
回数 0 2 4 4 10 4 4 0 0
増分 0 2 2 0 6 -6 0 -4 0
スコア 0 4 8.5 8.5 20 9.5 6.5 0 -1

タグB

0:00 0:10 0:20 0:30 0:40 0:50 1:00 1:10 1:20
回数 4 4 4 4 4 4 4 4 4
増分 0 0 0 0 0 0 0 0 0
スコア 8 8 8 8 8 8 8 8 8

増分を計算に含めることで、一気に盛り上がったタグの値を急上昇させることができます。(タグAの0:40)
前期、前々期の値を計算に使用することで、使用回数が減少した場合でも、スコアの下落をある程度なだらかにすることができます。(タグAの0:50)
常に同じペースで使用されるハッシュタグは増分が0に近くなるため、同じ使用回数であっても、前期から回数が増えた(盛り上がった)場合に比べてスコアが低くなります。(0:20と0:30)

より精度の良い計算式募集中です

APIについて

GET https://imastodon.net/api/v1/trend_tag
でトレンドタグの集計結果をJSON形式で返します。アクセストークンは不要にしています。
trend
なお、過去30分間にハッシュタグが一切使われていない場合は以下のようなレスポンスになります。
trend2

s = n + (n - l) + (l * 3 / 4.0) + ((l - b) / 2.0) + (b / 4.0)
tag = Tag.find(k.to_i)
trend_score[tag[:name]] = s
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的な好みが大きいですが、ここの重み付けのロジックは
個別にメソッドを分けて、ユニットテストをしやすいようにした方がいいかと思います。
汎用的なスコア算出のロジッククラスに出来そうであれば、
別クラスにするのもありだと思います。

ご検討をお願い致します。

end

def status_ids_in(t_min)
statuses = Status.where(created_at: (Time.now - t_min.minutes)..Time.now, local: true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time.now も引数にするのはどうでしょうか?

@fvh-P
Copy link
Collaborator Author

fvh-P commented Nov 10, 2017

ちょっと見直したところかなり無駄なコードが多かったので、Reviewに基づく修正を兼ねてリファクタリングをしました。やってることは特に変わりません。

@@ -27,3 +27,6 @@
ip_cleanup_scheduler:
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
class: Scheduler::IpCleanupScheduler
trend_tag_scheduler:
cron: '0,10,20,30,40,50 * * * *'
Copy link

@rinsuki rinsuki Nov 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/10 * * * *のような書き方にしたほうが簡潔でわかりやすいかと思います。
また、単純に一定間隔で実行したいだけならばcron:を使うのではなくevery:を使ってシンプルに書ける方法もあるようです。(これだとx0分に実行の要件がクリアできませんでした)
sidekiq-schedulerのREADMEを見てみてください。

@@ -27,3 +27,6 @@
ip_cleanup_scheduler:
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
class: Scheduler::IpCleanupScheduler
trend_tag_scheduler:
cron: '5-55/10 * * * *'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元の仕様に合わせるなら */10 になると思いますけど…。
5-55/10 だと 5,15,... になるけど意図的ですか?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

意図的なものです。
先日アイマストドンにて話したんですが、アイマス関連番組(ラジオなど)は0分、30分などキリの良い時間に開始・終了するものが大多数で、*/10だと0分では全く反映されず、30分ではしっかり反映されるもののすでに終わっているということになってしまうので、5-55/10にするのがいいのではないかということです。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

了解です。意図的であれば問題ないです。

@fvh-P
Copy link
Collaborator Author

fvh-P commented Nov 11, 2017

以前もお話ししたように、トレンドスコアの算出方法について、定量的な妥当性の検証が不可能に近いため、機能を稼働させつつ調整をするしかないのですが、いっそのこと試験的に同時に2つの算出方法を使って結果を公開するのが良いのではないかと思ったので2つ目の算出関数を追加しました。

需要予測に用いられる二重指数平滑法を使用して、次期の需要予測値をトレンドスコアとして利用します。水準の重みαとトレンドの重みγはあらかじめ自分で用意したデータを使って良さそうな組み合わせを探して設定しています。

APIでは "score_ex": {"tag1": 1.2}のような項目が追加されます。

導入後ある程度の期間試した後にどちらにするかなど検討する形になるかと思います。

追記:
試験導入が終了しscore_exの算出・公開を停止するときには、Redis上のscore_ex関連のデータを手動で削除していただくことになるかと思われます。

Copy link
Member

@takayamaki takayamaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メソッドの分け方とか少しリファクタリングしたい感ありますが、とりあえず出してみたいのでこれで取り込みます

tag = Tag.find(k.to_i)
trend_score[tag[:name]] = score(now: n, last: l, before: b)
end
redis.hmset('trend_tag', 'updated_at', Time.now.utc.iso8601, 'score', trend_score.to_json, 'last', now.to_json, 'before', last.to_json)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この辺、redisへのhmsetと計算そのものと外から呼び出されるメソッドとは別々にしたいところです

trend_now[k] = st.round(3)
trend_score_des[tag[:name]] = (sl + st).round(3)
end
redis.hmset('trend_tag', 'score_ex', trend_score_des.to_json, 'level_L', level_now.to_json, 'trend_L', trend_now.to_json)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここも上と同様

@takayamaki takayamaki merged commit d409333 into imas:imastodon Nov 16, 2017
@fvh-P fvh-P mentioned this pull request Nov 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants