Más contenido relacionado Similar a Shizuokapy4_データヴィジュアライズのための簡単なWeb API開発まめ知識 (20) Shizuokapy4_データヴィジュアライズのための簡単なWeb API開発まめ知識3. 今回の資料は…
• 特に、とにかく簡単にWeb APIを作ってみたい!とい
う人向けです。
• データセットを見てどのように実装できるか、デザイン
できるかなど、作業を見通すためにも、普段サービス構
築に関わらない人もAPIの理解を深めることがサービス
構築にとても立つはず。
• PythonのORマッパーつかうと簡単にできます。
4. つぶやきの音楽情報集めてます
• Twitterで#nowplaying されたつぶやきを集めて、アーティスト、楽
曲のランキングを作っています。(beatcaster.net)
• 音楽そのものの価値に加えて、コンテンツへの言及やユーザーの関わ
り方がコンテンツ波及の重要な要素になると考えています。ユーザー
のコンテンツへの関わり(コンテクスト)の情報がさらにコンテン
ツの消費を広げる要素になるような情報配信を模索しています。
• TWでは、季節に関連して集中して聞かれる(つぶやかれる)曲を見
かけます。「この日にこの曲がよく聞かれる」というような情報がコ
ンテンツの波及に有効かも。
5. 試しに「この日に聞かれる音楽」の
Web APIを作ってみました
• 日本語のつぶやきに限定して、1日に27000~2900程
度の音楽がTwitterで#nowplayingされます。このつぶ
やきを2年分集計しました。
• つぶやきから機械的に曲名、アーティスト名抽出をして
います。正規化していない…のでノイズ多めです。
• 『つぶやきの量』&TFIDFで『特定の日の特定の曲のつ
ぶやきの重みの指標』をJSONで返します。
6. musicinfo_count
DataFrame
特定日のTWをカウント
NPs
(ある日の総TW数)
DataFrameで集計
ある曲のTWが有った日
をカウント
Days
(TWのあった日数)
tfidfの値をupdate
ただし10TW/day以
上の曲に限る。
musicinfo_all
MySQL
TW数をカウント
musicinfo_count
MySQL
tfidf = (count/NPs) * log( 1/(Days/365) )
TFIDF集計プロセス
7. musicinfo_count
day: VARCHAR
count: INT
song: VARCHAR
artist: VARCHAR
tfidf: FLOAT
import_ja
user: VARCHAR
id: BIGINT
day: VARCHAR
txt: VARCHAR
musicinfo
song/artist 抽出:
musicinfo_all
user: VARCHAR
date: DATE
song: VARCHAR
artist: VARCHAR
txt: VARCHAR
count:
count
1日に10件以上のデータのある
song-artistの組み合わせのみ登録
tfidf集計:
get tfidf
update
category
artist: VARCHAR
category: VARCHAR
API用データベースの概念モデル
8. df = pd.DataFrame(dic, columns=['id', 'count', 'tfidf'])
plt.scatter(df['count'], df['tfidf'])
plt.show()
TW数
TFIDF
9. APIサーバを簡単に作るために…
ORマッパーを使います。
• Webサービス構築にORMを使うとデータベースとの接
続処理を専用のクラスに任せ、その処理を意識せずオブ
ジェクトの追加や取り出しができるようになります。
• 今回の例では自分が軽量フレームワークのFlaskとのセッ
トでよく使っている”SQLAlchemyで”サンプルのサービ
スを作っています。
10. モデルの定義とセッションの初期化
テーブル定義とクラスマッピング
from sqlalchemy import Table, Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Music(Base):
__tablename__ = 'musicinfo_count'
id = Column(Integer, primary_key=True)
day = Column(String(12))
song = Column(String(100))
artist = Column(String(200))
count = Column(Integer)
tfidf = Column(Float)
def __init__(self, id, day, song, artist, count, tfidf):
self.id = id
self.day = day
self.song = song
self.artist = artist
self.count = count
self.tfidf = tfidf
class ArtistCategory(Base):
__tablename__ = 'artist_category'
id =Column(Integer, primary_key=True)
artist = Column(String(200))
category = Column(String(10))
def __init__(self, artist, category):
self.artist = artist
self.category = category モジュールを作りアプリケーション内で使いまわします。
11. モデルの定義とセッションの初期化
セッションの作成
##~いろいろ省略
from sqlalchemy import create_engine, Date, and_, or_
from sqlalchemy.orm import sessionmaker, join
from music import Music, Base, ArtistCategory
from marshmallow import fields, Schema, Serializer
import json
##~いろいろ省略
!
Session = sessionmaker()
engine = create_engine('mysql://root:xxx@localhost/xxx',encoding='utf-8)')
Base.metadata.create_all(engine)
Session.configure(bind=engine)
session = Session()
SQLAlchemy のSessionは「ORMとデータベースとの対話を全て担当して、データベース
から読み出したり生成したマッピングインスタンスを保存しておく場所」とのこと。
12. ORMを使ったデータの取得
単純なselect
SQL では
select * from Music;
!
SQLAlchemyでは
result = session.query(Music).all()
!
※SQLAlchemyでは、Sessionのquery() メソッドを
使ってQueryオブジェクトを生成します。
13. ORMを使ったデータの取得
filterによる条件指定のパターン
• 単純な問い合わせ。all()を実行することでDBに実際に問い合わせが発生する。
session.query(Model).filter(Model.objectname == “hoo”).all()
• カンマ区切りでAND条件を指定
session.query(Model).filter(Model.object1==“hoo”, Model.object2==‘’bar”
• filterメソッドの複数指定でAND条件
session.query(Model).filter(Model.id==1).filter(Model.name==“hoo”).allI()
• OR条件
session.query(Model).filter(or_(Model.id==1, Model.name==“foo”).all()
• ORとANDの組み合わせ
session.query(Model).filter(or_(Model.id==1, Model.name==“hoo”), Model.value==1).all()
• SQL文を指定
session.execute(“SELECT * from musicinfo_count”)
• LIKEによる部分一致
session.querry(Model).filter(Model.name.like(‘%hoge%’).allI()
• IN演算子
session.query(Model).filter(Model.id.in_([1,2,3])).all()
• BETWEEN演算子
session.query(Model).filter(Model.value.between(1,3)
15. ORMを使ったデータの取得
marshmallowによるデータのシリアライズ
from marshmallow import fields, Schema, Serializer
import json
!
##~諸々省略
@app.route('/feature')
def getRanking():
days = request.args.get("byday")
if days:
mu = session.query(Music).filter(Music.day.like('%'+ days +’%')).
order_by(Music.tfidf.desc()).all()
serialized = muserializer(mu, many=True).data
return json.dumps(serialized, ensure_ascii=False).encode('utf-8')
!
class muserializer(Serializer):
class Meta:
fields = ("song", "artist","count", "tfidf")
16. musicinfo_count
day: VARCHAR
count: INT
song: VARCHAR
artist: VARCHAR
tfidf: FLOAT
import_ja
user: VARCHAR
id: BIGINT
day: VARCHAR
txt: VARCHAR
musicinfo
song/artist 抽出:
musicinfo_all
user: VARCHAR
date: DATE
song: VARCHAR
artist: VARCHAR
txt: VARCHAR
count:
count
1日に10件以上のデータのある
song-artistの組み合わせのみ登録
tfidf集計:
get tfidf
update
category
artist: VARCHAR
category: VARCHAR
API用データベースの概念モデル
17. musicinfo_count
marshmallow
query
category
JSON生成のプロセス
day: VARCHAR
count: INT
song: VARCHAR
artist: VARCHAR
tfidf: FLOAT
SQLAlchemy
filter()
API (getRanking)
song:
artist:
count:
tfidf:
category:
json
byday: %m-%d
d: %d
m: %m
c:[idol, voice actor, その他]
artist: VARCHAR
category: VARCHAR
19. 別テーブルの項目の追加
queryのリスト化
複合的な条件指定にクエリをリスト化する方法があります。
検索条件が複雑になったときのフィルタを書きやすいかも。
def getRanking2():
d = request.args.get("d")
m = request.args.get("m")
c = request.args.get("c")
params = []
if d:
md = m + "-" + d
params.append(Music.artist==ArtistCategory.artist)
params.append(Music.day==md)
params.append(ArtistCategory.category==c)
p = and_(*params)
mu = session.query(Music).filter(p).all()
serialized = mucatserializer(mu, many=True).data
return json.dumps(serialized, ensure_ascii=False).encode('utf-8')
filterの条件をリストに追加し
and_メソッドで条件を結合した後
リストをfilterメソッドに渡します。
20. 別テーブルの項目の追加
シリアライザで項目をJSONに追加する
class mucatserializer(Serializer):
category = fields.Method("get_category")
def get_category(self, Music):
ar = Music.artist
return getcategory(ar)
class Meta:
fields = ("song", "artist", "count", "tfidf", "category")
def getcategory(artist):
name = session.query(ArtistCategory).filter(Music.artist==ArtistCategory.artist)
.filter(Music.artist==artist).all()
if name:
return name[0].category
else:
return ""
シリアライザ内でArtistCategoryテーブルの
結合した項目を取得し追加ます。
21. 別テーブルの項目の追加
AND と ORの複合条件
if ct:
cts = ct.split(',')
if len(cts) == 1:
if "other" in ct:
params.append(ArtistCategory.category=="")
elif "VA" in ct:
params.append(ArtistCategory.category=="VA")
elif "IDOL" in ct:
params.append(ArtistCategory.category=="IDOL")
elif len(cts) == 2:
param = (ArtistCategory.category==cts[0])
for item in cts:
param = param | (ArtistCategory.category==item)
params.append(param)
md = m + "-" + d
params.append(Music.artist==ArtistCategory.artist)
params.append(Music.day==md)
p = and_(*params)
mu = session.query(Music).filter(p).all()
serialized = mucatserializer(mu, many=True).data
return json.dumps(serialized, ensure_ascii=False).encode('utf-8')
OR条件でリストに追加
AND条件でリストに追加