tag:crieit.net,2005:https://crieit.net/tags/annict/feed 「annict」の記事 - Crieit Crieitでタグ「annict」に投稿された最近の記事 2020-05-08T19:25:38+09:00 https://crieit.net/tags/annict/feed tag:crieit.net,2005:PublicArticle/15888 2020-05-08T19:25:38+09:00 2020-05-08T19:25:38+09:00 https://crieit.net/posts/b3c547c34df6393a1f86f8072aaf510a アニメのレコメンドサービスを作りました。 <p><a href="https://crieit.now.sh/upload_images/b0f24117575660588f657c26c91d370e5eb525f79a7b2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b0f24117575660588f657c26c91d370e5eb525f79a7b2.png?mw=700" alt="" /></a></p> <h1 id="サービスURL"><a href="#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9URL">サービスURL</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://annict-suggest.netlify.app/">https://annict-suggest.netlify.app/</a></p> <p><a href="https://crieit.now.sh/upload_images/102e92a702e89059033b1dff5b0f87c55eb5336deb10d.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/102e92a702e89059033b1dff5b0f87c55eb5336deb10d.jpg?mw=700" alt="" /></a></p> <h1 id="アニメの類似性をどう計算するか"><a href="#%E3%82%A2%E3%83%8B%E3%83%A1%E3%81%AE%E9%A1%9E%E4%BC%BC%E6%80%A7%E3%82%92%E3%81%A9%E3%81%86%E8%A8%88%E7%AE%97%E3%81%99%E3%82%8B%E3%81%8B">アニメの類似性をどう計算するか</a></h1> <h2 id="コサイン類似度"><a href="#%E3%82%B3%E3%82%B5%E3%82%A4%E3%83%B3%E9%A1%9E%E4%BC%BC%E5%BA%A6">コサイン類似度</a></h2> <p>人工知能を使わずにアニメのレコメンドサービスを作ろうと思ったのがきっかけです。<br /> <a href="https://crieit.net/posts/5308d8a3ed140ecc15e1310dad28e9e9">ユークリッド距離は触ったことがある</a>ので、他の指標としてコサイン類似度が面白そうだと思いました。<br /> ユークリッド距離は2点間の距離、コサイン類似度は2点のベクトル同士の角度です。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.albert2005.co.jp/knowledge/data_mining/cluster/cluster_summary">クラスター分析の手法①(概要)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tetsutaroendo/items/61942d25ae2a017831f2">コサイン類似度を利用し、集団の類似性を測ってみる</a></li> </ul> <h3 id="使った指標"><a href="#%E4%BD%BF%E3%81%A3%E3%81%9F%E6%8C%87%E6%A8%99">使った指標</a></h3> <p>約3300の作品に対して「見た」「見てない」のベクトルを作ってコサイン類似度を算出しようとしました。<br /> 「あにこれ」のように成分分析されているタグの類似度を計算するのもありだと思いました。</p> <h3 id="挫折"><a href="#%E6%8C%AB%E6%8A%98">挫折</a></h3> <p>導入は比較的楽なように思えたのですが、計算量が尋常ではありませんでした。事前にフィルタリングを何もかけていなかったため、3300レコードx3300レコードの計算をしようとしていて、あまりに時間がかかるので諦めました。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/harperfu6/items/3238d8f78c8a8d8cf114">アイテムの類似性について考察してみる</a></li> </ul> <h3 id="結局SQL"><a href="#%E7%B5%90%E5%B1%80SQL">結局SQL</a></h3> <p>例:ID4342の作品を見たユーザを抽出して、<br /> それらのユーザが他に見た作品のうち共通している人数が多い順に30件を取得する。</p> <pre><code class="sql">select sum(st2.watch_status) as count, st2.work_id, w.title from status st2 -- ID:4342の作品を見たユーザを取得 inner join ( select st.user_id from status st where st.work_id = 4342 )st3 on st2.user_id = st3.user_id inner join works w on w.annict_id = st2.work_id -- 作品自身を除く where st2.work_id != 4342 group by st2.work_id order by count desc limit 30 </code></pre> <h1 id="今回使った技術"><a href="#%E4%BB%8A%E5%9B%9E%E4%BD%BF%E3%81%A3%E3%81%9F%E6%8A%80%E8%A1%93">今回使った技術</a></h1> <ul> <li>GraphQL(Annict API)</li> <li>ReactJS</li> <li>NodeJS(TypeScript)</li> <li>twitterAPI</li> <li>netlify</li> </ul> <h2 id="データの棲み分け"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E6%A3%B2%E3%81%BF%E5%88%86%E3%81%91">データの棲み分け</a></h2> <p>最新のデータが欲しい場合はAnnictAPI(GraphQL)、分析データが欲しい場合はDBから読み込み、というようにデータの棲み分けを行っています。</p> <h2 id="GraphQLでエイリアスを使う"><a href="#GraphQL%E3%81%A7%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E3%82%92%E4%BD%BF%E3%81%86">GraphQLでエイリアスを使う</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://developers.annict.jp/graphql-api/reference/">Annict API</a><br /> ユーザが見たアニメと見ているアニメ両方が欲しい場合、<br /> エイリアスを使うと複数条件が記述できる。</p> <pre><code class="javascript">query { user(username:"${username}"){ annictId, works(state:WATCHED){ nodes{ annictId title } } ing:works(state:WATCHING){ nodes{ annictId title } } } } </code></pre> <h2 id="CSP(コンテンツセキュリティポリシー)"><a href="#CSP%28%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%29">CSP(コンテンツセキュリティポリシー)</a></h2> <p>アニメのOGPがない場合は公式twitterアカウントの画像を使用しているが、CSPなどで同じサイトでないコンテンツは表示できなくなったので、<br /> URLに「twitter」が含まれる場合はサーバにプロキシさせて画像を読み込むようにした。</p> <h1 id="ご意見"><a href="#%E3%81%94%E6%84%8F%E8%A6%8B">ご意見</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://ikens.net/ckoshien_tech/annict-suggest?v=1">こちら</a>から使ってみた感想・ご意見をお寄せください。</p> ckoshien tag:crieit.net,2005:PublicArticle/14995 2019-05-18T21:52:15+09:00 2019-05-18T21:52:15+09:00 https://crieit.net/posts/ReactNative ReactNativeアプリを作り始めました。 <h1 id="背景"><a href="#%E8%83%8C%E6%99%AF">背景</a></h1> <p>アニメ視聴遅れ管理サービスを作っているのですが、<a href="https://crieit.net/boards/annict-access/RN">ReactNativeアプリ</a>で好きなアニメランキングを作りはじめました。<br /> これがランキングを作る際の番組選択画面にあたります。<br /> (warningが出てるのは許してください....。)</p> <p><a href="https://crieit.now.sh/upload_images/73aa3ebbbfecf178ae23f56d1cbdbed45cdffa0224711.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/73aa3ebbbfecf178ae23f56d1cbdbed45cdffa0224711.jpg?mw=700" alt="image" /></a></p> <h1 id="GraphQLでサーバからデータを取得する"><a href="#GraphQL%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%90%E3%81%8B%E3%82%89%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B">GraphQLでサーバからデータを取得する</a></h1> <p>以前、<a href="https://crieit.net/posts/React-GraphQL-API">ReactでGraphQL APIにアクセスする</a>という記事を書いたので今回は割愛します。</p> <p>今回作成したクエリはこちら。<br /> <strong>${year}</strong> で取得したい年を渡しています。<br /> クエリの内容としては、${year}で指定した番組のタイトル、画像URL、annict上の番組ID、シーズンを視聴者数順にソートして返します。</p> <pre><code class="javascript">let query = gql`{ searchWorks( seasons:["${year}-spring","${year}-summer","${year}-autumn","${year}-winter"], orderBy: { field: WATCHERS_COUNT, direction: DESC } ){ edges{ node{ title image{recommendedImageUrl} annictId seasonName seasonYear } } } }` </code></pre> <h1 id="長すぎるタイトルを省略する"><a href="#%E9%95%B7%E3%81%99%E3%81%8E%E3%82%8B%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%82%92%E7%9C%81%E7%95%A5%E3%81%99%E3%82%8B">長すぎるタイトルを省略する</a></h1> <p>TextコンポーネントにこんなPropsがあるらしい。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/kondoakio/items/5a27aaf8e6a57b1fc106">【React Native】三点リーダーでテキストを省略</a></p> <p>今回使ったProps。</p> <pre><code class="javascript">numberOfLines={3} ellipsizeMode="tail" </code></pre> <h1 id="番組データを3カラムで表示する"><a href="#%E7%95%AA%E7%B5%84%E3%83%87%E3%83%BC%E3%82%BF%E3%82%923%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%A7%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">番組データを3カラムで表示する</a></h1> <p>deprecatedになっているListViewを使っているところは気になりますが、FlatListに読み替えてしまえばかなりの良記事だと思います。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/mat_aki/items/2db69acf61cf15ad70de">React Native の ListView で2カラムの表示を簡単に</a></p> <h1 id="番組画像に作品タイトルをオーバーレイさせる"><a href="#%E7%95%AA%E7%B5%84%E7%94%BB%E5%83%8F%E3%81%AB%E4%BD%9C%E5%93%81%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%82%92%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%AC%E3%82%A4%E3%81%95%E3%81%9B%E3%82%8B">番組画像に作品タイトルをオーバーレイさせる</a></h1> <p>元々UIに疎かった私。<br /> 画像に半透明のレイヤーを被せてその上に文字を表示するのを何というかということも知らなかったので、「画像 文字 のせる」というところからググりはじめて「オーバーレイ」と呼ぶことを知ったので、「react native overlay text」でググると...。<br /> <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/49250047/how-to-place-a-text-over-image-in-react-native">How to place a text over image in react native?<br /> </a></p> <p>Imageコンポーネントはchildrenを持てないようなので、<br /> <strong>ImageBackGround</strong>コンポーネントを使います。</p> <h1 id="実装まとめ"><a href="#%E5%AE%9F%E8%A3%85%E3%81%BE%E3%81%A8%E3%82%81">実装まとめ</a></h1> <p>今回行った実装の一部です。<br /> Imageコンポーネントに画像URLを渡していますが、<br /> 空文字やnullなどは怒られてしまうので条件分岐をさせて回避します。<br /> これで作品画像にタイトルがオーバーレイしたリストを3カラムで表示させることができます。</p> <pre><code class="javascript">return ( <FlatList style=<span>{</span><span>{</span> flex: 1, paddingTop: 20, backgroundColor: '#dddddd' <span>}</span><span>}</span> contentContainerStyle=<span>{</span><span>{</span> flexDirection: 'row', flexWrap: 'wrap' <span>}</span><span>}</span> data={store.getState().data} renderItem={(rowData)=>{ let image if(rowData.item.node.image !== null){ image = ( <ImageBackground style={ { width: (Dimensions.get('window').width - 20) / 3 - 20, height: 80, marginTop:10, marginBottom:10, marginLeft:10, marginRight:10 } } source={ { uri: rowData.item.node.image.recommendedImageUrl } }> <View style={ { position: 'absolute', //width: '100%', bottom: 0, justifyContent: 'center', alignItems: 'center'<span>}</span><span>}</span>> <Text style=<span>{</span><span>{</span> fontWeight:'bold', backgroundColor: 'rgba(255, 255, 255, 0.8)', width:(Dimensions.get('window').width - 20) / 3 - 20 <span>}</span><span>}</span> numberOfLines={3} ellipsizeMode="tail" >{rowData.item.node.title}</Text> </View> </ImageBackground> ) }else{ image = null } return ( <View style={ { padding: 1, backgroundColor: 'white', margin: 2, width: (Dimensions.get('window').width - 20) / 3, height: 100 } } > {image} </View> ) <span>}</span><span>}</span> /> ) </code></pre> ckoshien tag:crieit.net,2005:PublicArticle/14895 2019-03-31T23:50:35+09:00 2019-04-01T09:55:15+09:00 https://crieit.net/posts/React-GraphQL-API ReactでGraphQL APIにアクセスする <p><a href="https://crieit.now.sh/upload_images/0db2bc9716c1a8284f3a9afd4702101b5ca0d1aec3480.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0db2bc9716c1a8284f3a9afd4702101b5ca0d1aec3480.png?mw=700" alt="image" /></a></p> <h1 id="背景"><a href="#%E8%83%8C%E6%99%AF">背景</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://developers.annict.jp/graphql-api/">AnnictのGraphQL API</a>からデータを取得したい!<br /> 参照:<a href="https://crieit.net/boards/annict-access">アニメ視聴遅れ管理サービスの開発</a></p> <h1 id="reactではどう実装するか"><a href="#react%E3%81%A7%E3%81%AF%E3%81%A9%E3%81%86%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E3%81%8B">reactではどう実装するか</a></h1> <p>Apollo Clientが便利なようです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/seya/items/e1d8e77352239c4c4897">Apollo Client + React 入門</a></li> </ul> <h2 id="必要なパッケージのインストール"><a href="#%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">必要なパッケージのインストール</a></h2> <pre><code class="cmd">npm install apollo-boost react-apollo graphql-tag graphql --save </code></pre> <h2 id="ApolloClientの作成"><a href="#ApolloClient%E3%81%AE%E4%BD%9C%E6%88%90">ApolloClientの作成</a></h2> <ul> <li>エンドポイントを設定します。</li> <li>アクセストークンをヘッダに設定します。</li> </ul> <pre><code class="javascript">//APIエンドポイント let url = let url = 'https://api.annict.com/graphql' const client = new ApolloClient({ uri: url, request: operation => { operation.setContext({ headers: { authorization: 'Bearer ' + code } }) } }) </code></pre> <h2 id="graphqlを記述する"><a href="#graphql%E3%82%92%E8%A8%98%E8%BF%B0%E3%81%99%E3%82%8B">graphqlを記述する</a></h2> <p>このクエリは、ログインしているユーザ <strong>(viewer)</strong> の<br /> 見た作品 <strong>(state:WATCHED)</strong> を シーズン別降順 <strong>(orderBy: {field: SEASON, direction: DESC)</strong> でフィルタリングして返すという内容です。</p> <pre><code class="javascript">let query = gql`{ viewer { works(state: WATCHED, orderBy: {field: SEASON, direction: DESC}) { nodes { title seasonName seasonYear } } } }` </code></pre> <p>当初はタイトル画像のようなクエリのアプローチをしていたのですが、annictのdiscordコミュニティでannict作者のshimbacoさんに「こう書くといいですよ」と教えていただきました。</p> <h2 id="最後に"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB">最後に</a></h2> <p>とりあえずコンソールにログを出力するところまで。<br /> これをstoreに入れて表示してreact dndでソートするところまでが目標。</p> <pre><code class="javascript">export const fetchWatchedTitles=(code)=>{ (async()=>{ let url = 'https://api.annict.com/graphql' let query = gql`{ viewer { works(state: WATCHED, orderBy: {field: SEASON, direction: DESC}) { nodes { title seasonName seasonYear } } } }` const client = new ApolloClient({ uri: url, request: operation => { operation.setContext({ headers: { authorization: 'Bearer ' + code } }) } }) let response = await client.query({ query }) if(response !== null){ console.log(response.data.viewer.works.nodes) } })().catch( error=>{ console.log(error); } ) } </code></pre> <h1 id="参考リンク"><a href="#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF">参考リンク</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/shimbaco/items/e3f2f8650b08e1e060bd">AnnictのGraphQL APIを使ってアニメデータを取得しよう</a></li> </ul> ckoshien