tag:crieit.net,2005:https://crieit.net/tags/%E8%BE%B2%E5%AE%B6/feed 「農家」の記事 - Crieit Crieitでタグ「農家」に投稿された最近の記事 2020-12-11T23:17:08+09:00 https://crieit.net/tags/%E8%BE%B2%E5%AE%B6/feed tag:crieit.net,2005:PublicArticle/16067 2020-09-23T18:54:15+09:00 2020-12-11T23:17:08+09:00 https://crieit.net/posts/IT-3-Python [Python]農家のITマーケティング試行錯誤日記3.商品価格の集計と分析を行う <h2 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h2> <p><a href="https://crieit.net/posts/IT-2-Python">前回</a>の続きです。今回がラストになります。<br /> 楽天市場から取得した商品データの価格分析をするための前処理をしたので、今回はいよいよ簡単な分析にトライします。</p> <ul> <li>楽天市場で販売されている野菜(今回はメークイン)の価格相場はどのくらいなのか?</li> <li>1kgあたりの金額はいくらが妥当なのか?</li> </ul> <p>といったことを、初歩的な分析スキルを使って出してみたいと思います。</p> <h2 id="方針"><a href="#%E6%96%B9%E9%87%9D">方針</a></h2> <p>目的を改めて整理すると「農産物を販売するにあたっての価格調査のため」です。<br /> 農産物価格についてkgごとに集計したりグラフ化してみたりすることで、「どのくらいの価格で販売するのが妥当なのか」をある程度の根拠を持って得たいな、というのが今回の目的でした。<br /> そのためにまず調べやすそうな楽天市場での相場を調べることで、この根拠の一つにしてみよう、という考えです。</p> <p>以下のような手順で見ていきます。</p> <h3 id="(1)集計してみる"><a href="#%EF%BC%88%EF%BC%91%EF%BC%89%E9%9B%86%E8%A8%88%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">(1)集計してみる</a></h3> <p>まずは入数ごとの商品価格の要約統計量(平均値、中央値など)を集計し、表にして見てみます。<br /> つまり、重さ(1kg, 2kg…)ごとの商品価格を具体的な数値で把握してみることで、今回の分析の第一歩とします。</p> <h3 id="(2)分布を確認してみる(可視化)"><a href="#%EF%BC%88%EF%BC%92%EF%BC%89%E5%88%86%E5%B8%83%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B%EF%BC%88%E5%8F%AF%E8%A6%96%E5%8C%96%EF%BC%89">(2)分布を確認してみる(可視化)</a></h3> <p>入数ごとのデータを使ってヒストグラムや散布図を描いてみて、入数と商品件数や、入数と商品価格の関係がどのような分布になるかを見てみることにします。<br /> また、商品価格の中央値に絞って可視化を行ってみることで、次の(3)の分析に繋げます。</p> <h3 id="(3)線形近似してパラメータを求める"><a href="#%EF%BC%88%EF%BC%93%EF%BC%89%E7%B7%9A%E5%BD%A2%E8%BF%91%E4%BC%BC%E3%81%97%E3%81%A6%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B">(3)線形近似してパラメータを求める</a></h3> <p>入数と価格の中央値の関係を1次関数(直線)と見立ててその式のパラメータを求めてみます。<br /> 先ほど入数ごとの価格の中央値に着目して可視化したことで「入数と商品価格は1次関数の関係(グラフ上で直線)になりそう」という推測が生まれます。これは実際に当てはめると、商品価格は「送料や箱代・作業代などのベースとなる金額」に「1kgあたりの金額」が加算されて商品代が決められる、と考えることができます。<br /> 関係式のパラメータを求めることで、その「ベースとなる金額」と「1kgあたりの金額」を算出してみます。</p> <h2 id="集計と分析"><a href="#%E9%9B%86%E8%A8%88%E3%81%A8%E5%88%86%E6%9E%90">集計と分析</a></h2> <p>集計に入る前に、データの準備をしておきます。<br /> <a href="https://crieit.net/posts/IT-2-Python">前回</a>の最後に保存した加工済みのデータをcsvファイルから読み込んでDataFrameに入れておきます。</p> <pre><code class="python">df = pd.read_csv('20200914_rakuten_mayqueen_2.csv') </code></pre> <h3 id="(1)集計してみる"><a href="#%EF%BC%88%EF%BC%91%EF%BC%89%E9%9B%86%E8%A8%88%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">(1)集計してみる</a></h3> <p>準備ができたらまずは、入数ごとの商品価格の要約統計量を調べてみます。<br /> 入数ごとの表にするために<code>groupby()</code>関数を使います。<br /> また、統計量の表示には<code>describe()</code>メソッドを使います。</p> <pre><code class="python"># 入数ごとの統計量をgroupbyで調べる df_by_kg = df.groupby('入数').describe()['商品価格'] df_by_kg </code></pre> <p><a href="https://crieit.now.sh/upload_images/22790d6c7a5ae2a746f675e427ab3ef75f6a170cb3cce.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/22790d6c7a5ae2a746f675e427ab3ef75f6a170cb3cce.png?mw=700" alt="image" /></a></p> <p>この表の<code>count</code>列を見てみると、1件〜2件しかデータがない行と、データ件数が多い行がそれぞれあるのがわかります。<br /> 例えば「じゃがいも1kgや2kgでの販売」はそもそも件数が少なく、「じゃがいも5kgや10kgでの販売」は件数が多い、ということがわかります。<br /> 箱の規格は大体決まっているので実際の現場からすると納得の数値なのですが、このようにデータからも確認することができました。</p> <p>この表から、必要な行だけ抽出しておきます。今回は件数が多い行のみを活用することにします。<br /> 残す行の入数の数値を直接指定しても良いのですが、後々スクリプトを再利用する可能性も考えて、ここでは5件以上ある行(3kg, 5kg, 10kg, 20kgが該当)を抜き出してみることにします</p> <pre><code class="python"># 必要な行だけ抜き出す(件数が5つ以上あるものだけ残す) df_by_kg = df_by_kg.loc[df_by_weight[('商品価格', 'count')]>5, :] df_by_kg </code></pre> <p><a href="https://crieit.now.sh/upload_images/89504b4eabe580fe947ef58ace0e420e5f6a195573fe8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/89504b4eabe580fe947ef58ace0e420e5f6a195573fe8.png?mw=700" alt="image" /></a></p> <p>さらにここから、列も必要なものだけを残します。<br /> 今回は比較したい列として、「件数<code>count</code>」「平均値<code>mean</code>」「最小値<code>min</code>」「中央値<code>50%</code>」「最大値<code>max</code>」を残すことにしました。</p> <pre><code class="python"># 必要な列だけ抜き出す df_by_kg = df_by_kg.loc[:, [ 'count', 'mean', 'min', '50%', 'max']] df_by_kg </code></pre> <p><a href="https://crieit.now.sh/upload_images/c50f6d296153e9aa4f58e462891aa78c5f6a1a0d1a1eb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c50f6d296153e9aa4f58e462891aa78c5f6a1a0d1a1eb.png?mw=700" alt="image" /></a></p> <p>これで、スッキリした集計結果の表が得られました。<br /> この表を元に、例として「じゃがいも10kg」の商品価格を見てみます。その統計量は、</p> <ul> <li>最小値(<code>min</code>) 1,399円</li> <li>最大値(<code>max</code>) 7,560円</li> <li>平均値(<code>mean</code>) 3,820円</li> <li>中央値(<code>50%</code>) 3,600円</li> </ul> <p>というような金額(送料・税込)になっていました。<br /> 相場感を掴むためには平均値か中央値を参考にすれば良さそうですが、最小値と最大値の幅が大きいことから全体のバラつきが大きそうだと考えられます。そのため、参考にする数値としては平均値ではなく<strong>中央値</strong>を見ていこう、と考えます(このバラつきは次の項目で可視化して見てみます)。</p> <p>ちなみに最安値のものが異様に安いので気になってみてみると、いわゆる「訳あり品」として超小玉のものが販売されているようでした。逆に最高値を見てみると特別高級なブランド品という訳でもなく一地域の商品だったのですが、2020年の6月〜8月上旬頃まではじゃがいもの相場が全国的に異様に高かったため、このような金額で出品されているのかなと思いました(あくまで推測です)。</p> <h3 id="(2)分布を確認してみる(可視化)"><a href="#%EF%BC%88%EF%BC%92%EF%BC%89%E5%88%86%E5%B8%83%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B%EF%BC%88%E5%8F%AF%E8%A6%96%E5%8C%96%EF%BC%89">(2)分布を確認してみる(可視化)</a></h3> <p>次は、データをもとにグラフを描いて可視化することで、よりデータを深くみていこうと思います。</p> <p>まずは「ヒストグラム」を表示してみます。<br /> ここでは入数(kg)と度数(いくつのデータがあるか)を図示します。</p> <p>ヒストグラムを表示するには、<code>matplotlib</code>というライブラリの<code>hist()</code>関数を用います。<br /> データとして集計前のDataFram(<code>df</code>)を指定します。<br /> オプションに<code>bins</code>を指定することで、ヒストグラムの帯の幅を変えています。ここでは1から30までの1刻みで件数を図示しています。</p> <pre><code class="python">import matplotlib.pyplot as plt # ヒストグラム(入数と件数の関係) plt.hist(df['入数'], bins=np.arange(1, 30)) plt.xlabel('Quantity(kg)') plt.ylabel('Count') plt.grid(True) plt.show() </code></pre> <p><a href="https://crieit.now.sh/upload_images/b61556ef5e043764adcc0ddf429638ec5f6a1b9caa4e1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b61556ef5e043764adcc0ddf429638ec5f6a1b9caa4e1.png?mw=700" alt="image" /></a></p> <p>先ほど数値で見たことの確認になりますが、入数kgによって件数に差がありますね。5kg、10kgあたりの商品が特に多く、3kgや20kgも比較的出ているというのがわかります。</p> <p>次に、「散布図」を表示してみます。<br /> 散布図を表示するには、<code>matplotlib</code>の<code>scatter()</code>関数を用います。<br /> これも集計前のDataFram(<code>df</code>)を指定してみます。</p> <pre><code class="python"># 散布図(入数と商品価格の関係) plt.scatter(df['入数'], df['商品価格']) plt.xlabel('Quantity(kg)') plt.ylabel('Pirce(yen)') plt.grid(True) plt.show() </code></pre> <p><a href="https://crieit.now.sh/upload_images/fe59440662f4b1c0c74f24e689ccc4a35f6a1bd479884.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/fe59440662f4b1c0c74f24e689ccc4a35f6a1bd479884.png?mw=700" alt="image" /></a></p> <p>何kgの商品がいくらで販売されているのかが点で表示されますが、ここで先ほど集計した時に触れた「価格のバラつき」が大きいことが目に見えてわかります。<br /> この状態だと、バラつきが大きくて値決めの判断材料にはできなさそうです。</p> <p>そこで、今度は商品価格の「中央値」に絞ってプロット図を描いてみます。<br /> 先ほどの散布図と違って、入数ごとに対応する商品価格の中央値となる1点がグラフ上にプロットされます。<br /> ここでは集計後のDataFrame(<code>df_by_kg</code>)を使って図示します。</p> <pre><code class="python"># 点をプロット(入数と商品価格(中央値)の関係) plt.plot(df_by_kg.index, df_by_kg['50%'], 'o') plt.xlabel('Quantity(kg)') plt.ylabel('Pirce(yen)') plt.grid(True) plt.show() </code></pre> <p><a href="https://crieit.now.sh/upload_images/ad77690da113f9a9075115694cd200c75f6a1c2b93588.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ad77690da113f9a9075115694cd200c75f6a1c2b93588.png?mw=700" alt="image" /></a></p> <p>プロットした点が、綺麗な直線上に並んでるように見えます。<br /> なんだか入数と商品価格の関係を掴むことができそうです。</p> <p>それぞれの点における数値(つまり中央値)は以下の通り。</p> <p><a href="https://crieit.now.sh/upload_images/955c9e30d9ba9618d1bfb05c33c273885f6a1c4718834.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/955c9e30d9ba9618d1bfb05c33c273885f6a1c4718834.png?mw=700" alt="image" /></a></p> <p>野菜の販売金額を設定する際には、このぐらいの金額に設定することで、ネット販売における相場とのズレが少ない妥当な値決めができそうです。<br />  <br />  <br /> このように、数値を見るだけでなく可視化すると直感的にわかりやすくなります。<br /> また可視化するにしても、漠然と全体を見るだけだとバラつきが多くてどう見て良いのかよくわからなかったものが、「中央値」という1つの統計量に絞って見てみると規則性を見ることができました。</p> <h3 id="(3)線形近似してパラメータを求める"><a href="#%EF%BC%88%EF%BC%93%EF%BC%89%E7%B7%9A%E5%BD%A2%E8%BF%91%E4%BC%BC%E3%81%97%E3%81%A6%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B">(3)線形近似してパラメータを求める</a></h3> <p>もう少し、先ほどの結果を深掘りしてみることにします。<br /> 先ほどプロットした入数kgに対する商品価格の中央値の値は、1次関数の関係に近い(グラフ上で点がほぼ直線上に並んでいる)と考えられます。<br /> そこで「線形近似」というのを行ってみます。<br /> 近似した直線の式のパラメータ(傾きと切片)を求めることで、商品価格を詳細に見ることができそうです。<br /> つまり、「1kgあたりの金額」はこの直線の傾きの値に該当し、「送料や箱代・作業代などのベースとなる金額」はこの直線の切片に該当します。</p> <p>線形近似にはnumpyの機能を使います。</p> <pre><code class="python"># 線形近似 linear = np.polyfit(df_by_kg.index, df_by_kg['50%'], 1) # 線形近似して切片と傾きを求める func = np.poly1d(linear) # 切片と傾きから1次式を作る x = df_by_kg.index y = func(x) # 線形近似したグラフ表示 plt.plot(x, y) # 散布図も合わせて表示 plt.plot(df_by_kg.index, df_by_kg['50%'], 'o') plt.xlabel('kg') plt.ylabel('yen') </code></pre> <p><a href="https://crieit.now.sh/upload_images/cfec2cf3ec2fef757b97492df98485bd5f6a1f7b19d61.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cfec2cf3ec2fef757b97492df98485bd5f6a1f7b19d61.png?mw=700" alt="image" /></a></p> <p>このように、プロットした点がだいたい直線に乗っていることが確認できるかと思います。<br /> そしてこの<code>linear</code>変数に、この直線の式のパラメータが入っています。</p> <pre><code class="python">print('直線のパラメータ[傾き, 切片] = ', linear) # -&gt; 直線のパラメータ[傾き, 切片] = [ 142.94797688 2071.99421965] </code></pre> <p>つまり、</p> <ul> <li>ベースとなる金額(切片に該当)約 2072円</li> <li>1kgあたり金額(傾きに該当) 約143円</li> </ul> <p>この数値を使うと、例えば4kgの商品を作るとしたら、2072円 + 4kg@143円 = 2,644円 くらいで設定すると良いんじゃないか、と計算することができます。</p> <p>商品価格の中央値が、ここまで綺麗に一次関数の式に近似できたのが面白かったです。<br /> 今回使用したデータ数は分析するには少なかったと思うのですが、それでも感覚的に納得できる数値を得ることができました。</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p>今回のような基礎的な分析でも、活用できそうな根拠のある数値を得ることができました。<br /> このような分析結果をもとに意思決定に繫げるのが、仕事でデータ分析を活用するにあたっての肝になるのかな、と思います。<br /> そしてそれは1回で終えるのではなく、トライ&エラーを重ねて、より正しく説得力のある分析と意思決定を行うサイクルを回していくことが大事になってくるようにも思います。</p> <p>ひとつ大事な観点として、分析結果の妥当性はもうちょっと検証する必要はありそうです。<br /> 例えばデータ数をもっと増やしたり、データの中身を精査する必要があります。今回の例では、品種によって細分化したらもっと性格なデータを出せるなあと思いました。</p> <p>データ分析したこの後は実際の価格決めという意思決定のフェーズに入っていくのですが、それにはまた別の視点が必要です。<br /> 少なくとも以下のことは、自身の中で固めておく必要があるなあと思います。</p> <ul> <li>商品の価値をどう設定するのか。相場より安く設定するのか高く設定するのか。</li> <li>ターゲットは誰なのか。今回楽天市場のデータで分析したが、よりターゲットに近い市場での分析が必要かもしれない。</li> </ul> <p>もうちょっとスピード感を持って意思決定できれば良いのでしょうけど、難しい。</p> <p>ひとまず今回の数値を使って、近々ひとつアウトプットを出すようにしたいと思います!</p> <p>さて、三回に渡ってデータ分析に関する記事を続けてきましたが、今回のテーマは一旦ここまでにします。<br /> 学んだ知識をもとに探り探りの分析だったため、もしかしたら考え方が間違っていたりよりスマートな記述の仕方があるかもしれません。<br /> ここまで読んでいただいて、もし気になる点があればコメントいただけると大変嬉しいです!</p> <h2 id="(記事リンク)農家のITマーケティング試行錯誤日記"><a href="#%EF%BC%88%E8%A8%98%E4%BA%8B%E3%83%AA%E3%83%B3%E3%82%AF%EF%BC%89%E8%BE%B2%E5%AE%B6%E3%81%AEIT%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E8%A9%A6%E8%A1%8C%E9%8C%AF%E8%AA%A4%E6%97%A5%E8%A8%98">(記事リンク)農家のITマーケティング試行錯誤日記</a></h2> <p><a href="https://crieit.net/posts/Python-IT-API-csv">農家のITマーケティング試行錯誤日記1.楽天商品検索APIを使って商品情報をcsv出力する[Python]</a><br /> <a href="https://crieit.net/posts/IT-2-Python">農家のITマーケティング試行錯誤日記2.商品情報の収集とデータ加工を行う[Python]</a><br /> <a href="https://crieit.net/ぽsts/IT-3-Python">農家のITマーケティング試行錯誤日記3.商品価格の集計と分析を行う[Python]</a></p> Massa tag:crieit.net,2005:PublicArticle/16058 2020-09-15T20:15:55+09:00 2020-12-11T23:18:09+09:00 https://crieit.net/posts/IT-2-Python [Python]農家のITマーケティング試行錯誤日記2.商品情報の収集とデータ加工を行う <h2 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h2> <p><a href="https://crieit.net/posts/Python-IT-API-csv">前回</a>は「楽天商品検索API」を使って30件(1ページ分)のデータを抽出し、csvファイルに保存するまでを行いました。</p> <p>今回はより実用的にするために、まず前回のスクリプトを発展させて取得するデータ件数を増やしたのちに、集計や分析の前段階として、商品データを商品重量ごと(5kg, 10kg...)に分類できるようなデータ加工まで行います。</p> <h2 id="方針"><a href="#%E6%96%B9%E9%87%9D">方針</a></h2> <h3 id="(1)商品情報を取得する"><a href="#%EF%BC%88%EF%BC%91%EF%BC%89%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B">(1)商品情報を取得する</a></h3> <ul> <li>検索キーワード「メークイン」商品情報を300件(30件×10ページ)取得する(NGワードも指定する)</li> <li>必要な商品情報のみを入れたdict型を作成する</li> <li>PandasのDataFrameに格納し、列を整理する</li> </ul> <p>前回と流れは同じですが、今回は取得件数とNGワードをここで指定しています。<br /> これらはデータを集める目的や、必要な情報量と中身に応じて調整します。</p> <p>NGワードは僕の場合は、自分の想定と合わず商品価格に影響してきそうなワードを指定しました。ここでは「有機栽培/オーガニック/減農薬/農薬不使用」「セット/詰め合わせ」「ふるさと納税」を除きます。<br /> 取得件数を300件としたのは特に深い意味はありませんが、今回のキーワード・NGワードで商品検索をかけたところ320件程度のヒット数だったため、キリの良い数字を選びました。</p> <h3 id="(2)データ加工の手順"><a href="#%EF%BC%88%EF%BC%92%EF%BC%89%E3%83%87%E3%83%BC%E3%82%BF%E5%8A%A0%E5%B7%A5%E3%81%AE%E6%89%8B%E9%A0%86">(2)データ加工の手順</a></h3> <ul> <li>収集したデータから必要なデータのみを抽出する</li> <li>「商品名」から重さ(5kg, 10kg...)を抽出し、新たな「入数」列を作る</li> </ul> <p>本来は野菜の品種ごとにまで分けた方が正確な金額が出せるのですが、今回はそこまではしないことにしました。あまり細分化しすぎるとデータ数が減ってしまうためです。</p> <h2 id="(1)商品情報を取得するスクリプト"><a href="#%EF%BC%881%EF%BC%89%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">(1)商品情報を取得するスクリプト</a></h2> <h3 id="1.必要なライブラリのインポートと入力パラメータの準備"><a href="#1.%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E5%85%A5%E5%8A%9B%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%BA%96%E5%82%99">1.必要なライブラリのインポートと入力パラメータの準備</a></h3> <p>ここは前回とほぼ変わりありませんが、APIでリクエストを送る入力パラメータの<code>page</code>と<code>NGKeyword</code>、そして<code>postageFlag</code>の部分のみ変えています。</p> <p>楽天商品検索APIの1回のリクエストで取得できるデータの上限は30件(入力パラメータの<code>hits</code>の値)となっています。<code>page</code>にページ数<code>2</code>を指定してやれば、31件目以降のデータが取得できます。<br /> この<code>page</code>の値を<code>for</code>文で回すことを想定しています。</p> <p>また、入力パラメータの<code>postageFlag</code>は商品価格に送料を含むかどうかを指定しています(1で送料含むor送料無料)</p> <pre><code class="python">import requests import numpy as np import pandas as pd REQUEST_URL = "https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706" APP_ID="<楽天APIのIDを入れる>" # 入力パラメータ serch_keyword = 'メークイン' ng_keyword = 'ふるさと納税 有機 オーガニック 減農薬 農薬不使用 セット 詰め合わせ' page = 1 serch_params={ "format" : "json", "keyword" : serch_keyword, "NGKeyword":ng_keyword, "applicationId" : [APP_ID], "availability" : 0, "hits" : 30, "page" : page, "sort" : "standard", "postageFlag" : 1 } </code></pre> <h3 id="2.必要な商品情報のみを入れたdict型を作成する"><a href="#2.%E5%BF%85%E8%A6%81%E3%81%AA%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%81%AE%E3%81%BF%E3%82%92%E5%85%A5%E3%82%8C%E3%81%9Fdict%E5%9E%8B%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">2.必要な商品情報のみを入れたdict型を作成する</a></h3> <p><code>for</code>文を使って複数にまたがるページの商品情報を取得していきます。</p> <p>商品情報から必要な項目のみ抜き出して<code>tmp_item</code>という名前のdictに格納し、それを<code>item_list</code>という名前のリストに格納しているのは、前回と同様の流れになります。</p> <pre><code class="python"># 商品情報をリストで取得 item_list = [] # 30件ずつ取得した辞書型の商品情報tmp_itemが10ページ分入る max_page = 10 for page in range(1, max_page+1): serch_params['page'] = page # APIにリクエストを送り、結果として商品データresultを得る response = requests.get(REQUEST_URL, serch_params) result = response.json() # resultから必要な情報を抜き出したdictを作る item_key = ['itemName', 'itemPrice', 'itemCaption', 'shopName', 'shopUrl', 'itemUrl'] for i in range(0, len(result['Items'])): tmp_item = {} item = result['Items'][i]['Item'] for key, value in item.items(): if key in item_key: tmp_item[key] = value item_list.append(tmp_item.copy()) </code></pre> <p>ここで、仮に商品件数が10ページ分に満たないようなキーワードを入れた場合でも、特にエラーは出ずに取得することができました。ページ数は実際の商品数よりも大きく設定しても良さそうです(APIのリファレンスによると<code>page</code>の上限は100)。<br /> ですがその分処理に時間がかかり、自分の環境だと稀にエラーが出てしまう現象が起きたので、必要最小限に留めた方が良いのかなと思います。</p> <h3 id="3.DataFrameを作成する"><a href="#3.DataFrame%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">3.DataFrameを作成する</a></h3> <p>これも前回と同様に、先ほどの商品情報のdictが格納されたlistからPandasのDataFrameを作成します。<br /> 少し修正して、インデックスを0からではなく1から順番に振ってくれるようにしました。</p> <pre><code class="python"># データフレーム作成 df = pd.DataFrame(item_list) df = df.reindex(columns=['itemName', 'itemPrice', 'itemCaption', 'itemUrl', 'shopName', 'shopUrl']) df.columns = ['商品名', '商品価格', '商品説明文', '商品URL', '店舗名', '店舗URL'] df.index = df.index + 1 # インデックスを1からに振り直す </code></pre> <p><code>df.count()</code> で取得件数、<code>df.head()</code>で先頭5件のデータを確認。<br /> 想定どおりの300件のデータが入っていれば大丈夫そうです。</p> <h3 id="4.csv出力"><a href="#4.csv%E5%87%BA%E5%8A%9B">4.csv出力</a></h3> <p>毎回このスクリプトを走らせてデータを取得するのも面倒なので、使いたい時に使えるようにcsv出力しておきます。</p> <pre><code class="python">df.to_csv('20200914_rakuten_mayqueen.csv') </code></pre> <h2 id="(2)データ加工・集計のスクリプト"><a href="#%282%29%E3%83%87%E3%83%BC%E3%82%BF%E5%8A%A0%E5%B7%A5%E3%83%BB%E9%9B%86%E8%A8%88%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">(2)データ加工・集計のスクリプト</a></h2> <h3 id="1.csv読み込み"><a href="#1.csv%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF">1.csv読み込み</a></h3> <p>データの件数を増やすことができたので、次はそのデータを加工して分析に適した形にしてみます。<br /> このまま進めても良いですが、いったん先ほどのcsvフィルを読み込んでみてから進めます。</p> <pre><code class="python">df = pd.read_csv('20200914_rakuten_mayqueen.csv') </code></pre> <h3 id="2.必要なデータのみを抽出する"><a href="#2.%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E3%81%BF%E3%82%92%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B">2.必要なデータのみを抽出する</a></h3> <p>このDataFrameを加工していくのですが、まずはざっとデータの中身をspread sheetで眺めてみると、今回の価格調査の目的には当てはまらない、野菜以外の余計なデータが混じっていました。<br /> 今回必要なのは重量のデータなので「商品名に重量が入っている商品データのみを残す」という方針でやってみます。</p> <p>Pandasで、指定した文字列を含む(部分一致)行を抽出してbool値で返す<code>str.contains()</code>を使います。</p> <pre><code class="python"># 商品名に「kg」が入っている商品データのみ残す kg_flg = df['商品名'].str.contains('kg') df = df[kg_flg] </code></pre> <p><code>kg_flg</code>はbool値のSeriesとなっていて、「kg」が含まれている行にはTrue,そうでない行にはFalseが入ります。Trueになっている行が必要な残したいデータです。<br /> これを用いて<code>df[kg_flg]</code>とすることで、Trueの行だけデータの入ったDataFrameを抽出することができます。</p> <p><code>df.count()</code>で件数を確認すると、116件とだいぶ減ってしまいました。<br /> よりデータ数を確保するなら、この辺りはもうちょっと検証する必要がありそうです。</p> <h3 id="3.商品名から重量を抽出して新たな列を作る"><a href="#3.%E5%95%86%E5%93%81%E5%90%8D%E3%81%8B%E3%82%89%E9%87%8D%E9%87%8F%E3%82%92%E6%8A%BD%E5%87%BA%E3%81%97%E3%81%A6%E6%96%B0%E3%81%9F%E3%81%AA%E5%88%97%E3%82%92%E4%BD%9C%E3%82%8B">3.商品名から重量を抽出して新たな列を作る</a></h3> <p>これで商品名にkgが含まれている行のみが残りましたが、さらにこの重量を別の列に切り出したいです。<br /> 商品名には「数字 + kg」という形で重量が入っているはずなので、これの数値を取り出して「入数」という列を新しく作ります。</p> <p>ここでは正規表現を使ってみます(詳細な説明は省きますが、<code>([0-9]+)kg</code>とすることで「数字 + kg」を表現できます)<br /> この正規表現をPandasの<code>str.extract()</code>の引数に指定します。このメソッドは引数に正規表現を指定すると、それに最初にマッチした文字列を抽出して新たな列を作る、というものです。まさに今回やりたいことにぴったりな便利メソッドです。</p> <pre><code class="python"># 商品名から重量を別カラムに切り出す df['入数'] = df['商品名'].str.extract('([0-9]+)kg') df =df.reindex(columns=['商品名', '入数', '商品価格', '商品説明文', '商品URL', '店舗名', '店舗URL']) df.to_csv('20200914_rakuten_mayqueen_2.csv') </code></pre> <p>見やすいように列の入れ替えも行い、最後にcsv出力をしておきます。<br /> こんな感じでDataFrameを作ることができました。</p> <p><a href="https://crieit.now.sh/upload_images/3a9a287d043e84762270b380660926425f609fb1250c2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3a9a287d043e84762270b380660926425f609fb1250c2.png?mw=700" alt="image" /></a></p> <p>画像では、この後の分析のために入数を数値型に変換したのちに、商品価格を入数で割返したkg単価も付け加えてみました。ですが結構バラバラの数値が出ているので、ここは次回もうちょっと掘り下げてみます。</p> <p>ともあれ、これで商品価格の分析のための下準備が終わったのではないでしょうか。</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p>分析のためには出来るだけ多いデータを集めた方が偏りが少ないと思うのですが、このやり方だと結構ざっくりと商品件数を削っており、もうちょっと上手いやり方がありそうだなと思いました。<br /> また、残ったデータも完璧なものと言えるのか定かではないので、細かいデータ分析をするにはまだ検証しなければいけないことが多いと思います。</p> <p>まあ今回は個人的にやっていることですし、まずはアウトプットできることを目指して、ある程度の納得感があれば良いなーくらいに思って、細かい部分にはこだわらず進めています。</p> <p><a href="https://crieit.net/drafts/IT-3-Python">次回</a>、データを集計して商品価格の平均値などの統計量を算出してみたり、可視化や線形近似を行ってみてkg単価を算出してみたりと、簡単な分析を行ってみようと思います。</p> <h2 id="(記事リンク)農家のITマーケティング試行錯誤日記"><a href="#%EF%BC%88%E8%A8%98%E4%BA%8B%E3%83%AA%E3%83%B3%E3%82%AF%EF%BC%89%E8%BE%B2%E5%AE%B6%E3%81%AEIT%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E8%A9%A6%E8%A1%8C%E9%8C%AF%E8%AA%A4%E6%97%A5%E8%A8%98">(記事リンク)農家のITマーケティング試行錯誤日記</a></h2> <p><a href="https://crieit.net/posts/Python-IT-API-csv">農家のITマーケティング試行錯誤日記1.楽天商品検索APIを使って商品情報をcsv出力する[Python]</a><br /> <a href="https://crieit.net/posts/IT-2-Python">農家のITマーケティング試行錯誤日記2.商品情報の収集とデータ加工を行う[Python]</a><br /> <a href="https://crieit.net/drafts/IT-3-Python">農家のITマーケティング試行錯誤日記3.商品価格の集計と分析を行う[Python]</a></p> Massa tag:crieit.net,2005:PublicArticle/16044 2020-09-01T22:20:21+09:00 2020-12-11T23:18:34+09:00 https://crieit.net/posts/Python-IT-API-csv [Python]農家のITマーケティング試行錯誤日記1.楽天商品検索APIを使って商品情報をcsv出力する <h2 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h2> <p>楽天市場のAPIを活用し、キーワードに当てはまる商品情報をcsvに出力してみました。</p> <p>活用したのはこちらの「楽天商品検索API」というものです。<br /> <a target="_blank" rel="nofollow noopener" href="https://webservice.rakuten.co.jp/api/ichibaitemsearch/">楽天ウェブサービス: 楽天商品検索API(version:2017-07-06) | API一覧</a></p> <h3 id="開発環境と使用するライブラリ"><a href="#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%A8%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA">開発環境と使用するライブラリ</a></h3> <p>開発環境としてJupyter Notebookを利用しました。<br /> 大掛かりなツールや定期的に実行したいツール作成時にはまた別のテキストエディタで作っていく必要があるかもしれませんが、ちょっとした単発のツールを作る際には、Jupyter Notebookなら少しずつ試しながらスクリプトが書けるし、即座に実行できるのでとても便利です。</p> <p>使用するライブラリは<code>request</code>と<code>pandas</code>。<br /> APIを叩くために<code>request</code>を使い、取得したデータ操作とcsv出力のために<code>pandas</code>を利用しました。</p> <h2 id="目的"><a href="#%E7%9B%AE%E7%9A%84">目的</a></h2> <p>農産物を販売するための価格調査のために行いました。<br /> 取得した情報をもとに、さらに分析して意思決定するところまでを想定しています(今回は情報取得するところまで)。</p> <p>色々な直販サイトはありますが、楽天市場は馴染みがあり商品数も多く、APIも提供されているので取得しやすいなと考えました。</p> <h2 id="楽天APIを扱う準備"><a href="#%E6%A5%BD%E5%A4%A9API%E3%82%92%E6%89%B1%E3%81%86%E6%BA%96%E5%82%99">楽天APIを扱う準備</a></h2> <p>APIを活用するためには、スクリプトを書き始める前にまずは楽天の開発者向けページからアプリを作成してIDを取得しなければいけません。</p> <p>こちらのRakuten Developersのサイト<br /> <a target="_blank" rel="nofollow noopener" href="https://webservice.rakuten.co.jp/document/#ichibaApi">楽天ウェブサービス: API一覧</a></p> <p>右上の「+アプリID発行」からアプリを作成しておきます。ここで取得したアプリIDを自分のスクリプトで実行する際に使うことで、楽天市場の情報にアクセスでき流ようになります。</p> <p>楽天市場だけでなく、楽天の他のサービス(楽天トラベルや楽天レシピなど)のAPIもあるのが良いですね。機会があれば使ってみたいです。</p> <h2 id="商品情報を取得するスクリプト"><a href="#%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">商品情報を取得するスクリプト</a></h2> <h3 id="(1)キーワードを入れて商品情報を取得してみる"><a href="#%281%29%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89%E3%82%92%E5%85%A5%E3%82%8C%E3%81%A6%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">(1)キーワードを入れて商品情報を取得してみる</a></h3> <p>今回は「メークイン」というじゃがいもの品種名をキーワードに含む商品情報を取得することにします。</p> <p>まずは必要なライブラリのインポート。</p> <pre><code class="python">import requests import pandas as pd </code></pre> <p>次にAPIを叩いて情報を取得するスクリプト。</p> <pre><code class="python">REQUEST_URL = "https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706" APP_ID="<ここに楽天のサイトで取得したアプリIDを入れる>" serch_keyword = 'メークイン' serch_params={ "format" : "json", "keyword" : serch_keyword, "applicationId" : [APP_ID], "availability" : 0, "hits" : 30, "page" : 1, "sort" : "-updateTimestamp" } response = requests.get(REQUEST_URL, serch_params) result = response.json() </code></pre> <p>このスクリプトを実行したのち、<code>result['Items']</code>とすれば、情報がdict型のlistの形で取得できます。<br /> 今回は30商品が取得できています(<code>serch_params</code>の<code>"hits":30</code>で指定している数値がこの取得数に当たります)</p> <p>先ほどのスクリプトをざっと見ておきます。</p> <p><code>REQUEST_URL</code>には<a target="_blank" rel="nofollow noopener" href="https://webservice.rakuten.co.jp/api/ichibaitemsearch/">楽天ウェブサービス: 楽天商品検索API(version:2017-07-06) | API一覧</a>に載っているリクエストURLを指定。<br /> <code>APP_ID</code>には先ほど楽天の開発者向けページから取得したアプリIDを記述しておきます。</p> <p><code>serch_keyword</code>で検索したい文字列を指定することで、そのキーワードにマッチする商品が検索されます。例えばここをPythonの<code>input()</code>関数でユーザー入力を受け付けても使い勝手が良さそうですね。</p> <p>次に、<code>serch_params</code>にはリクエストを送る際のパラメータをdict型で書いておきます。<br /> <a target="_blank" rel="nofollow noopener" href="https://webservice.rakuten.co.jp/api/ichibaitemsearch/">楽天ウェブサービス: 楽天商品検索API(version:2017-07-06) | API一覧</a>の「入力パラメーター」の項に詳細が載っていますので、詳細はそちらを参照してみてください。<br /> 注意点として、この入力パラメーターには<code>applicationId</code>(アプリID)は必須でこれには先ほど記述した<code>APP_ID</code>を指定、また<code>keyword</code>,<code>shopCode</code>, <code>itemCode</code>, <code>genreId</code>のいずれかの指定が必須のようです。<br /> 今回は検索キーワードで商品情報を取得したいので、<code>keyword</code>に先ほど指定した<code>serch_keyword</code>を指定しました。</p> <p>この入力パラメータの<code>"hits":30</code>というのが取得する商品の件数に当たり、最大値が30のようです(省略してもデフォルトで30が設定されているようですが、今回のスクリプトでは忘れないように明示しておきました)。<br /> また<code>"page":1</code>というのが取得ページ番号になるので、この数値をfor文などでループを回せば複数ページに渡る大量の商品情報を簡単に取得できそうですね。</p> <h3 id="(2)必要な商品情報を入れたdict型を作成"><a href="#%282%29%E5%BF%85%E8%A6%81%E3%81%AA%E5%95%86%E5%93%81%E6%83%85%E5%A0%B1%E3%82%92%E5%85%A5%E3%82%8C%E3%81%9Fdict%E5%9E%8B%E3%82%92%E4%BD%9C%E6%88%90">(2)必要な商品情報を入れたdict型を作成</a></h3> <p>さて、先ほどAPIを叩いて取得できたdictには<a target="_blank" rel="nofollow noopener" href="https://webservice.rakuten.co.jp/api/ichibaitemsearch/">楽天ウェブサービス: 楽天商品検索API(version:2017-07-06) | API一覧</a>の「出力パラメーター」の項に載っている項目がdictのkeyとvalueとして入っています。</p> <p>例えば<code>result['Items'][2]['Item']['itemName']</code>という風にkeyを指定すると、商品名が取得できます。</p> <p>現段階で取得した情報は、このままだと余計な情報も入っていて扱いが不便なので、必要な情報のみが入っているdictを作ることにします。以下の項目に絞りました。</p> <p>「itemName」「itemPrice」「itemCaption」「shopName」「shopUrl」「itemUrl」<br /> (後から、送料フラグ「postageFlag」も必要だなと思ったのですが、以下のスクリプトには反映されておりません)。</p> <pre><code class="python"># for文を回してdictのlistを作る item_key = ['itemName', 'itemPrice', 'itemCaption', 'shopName', 'shopUrl', 'itemUrl'] item_list = [] for i in range(0, len(result['Items'])): tmp_item = {} item = result['Items'][i]['Item'] for key, value in item.items(): if key in item_key: tmp_item[key] = value item_list.append(tmp_item) </code></pre> <p>これで、dict型の商品情報の入ったlistが取得できます。</p> <p>ここで詰まったのが、<code>item_list.append(tmp_item.copy())</code>のところで<code>copy()</code>メソッドを使う必要があることでした。<br /> このメソッドを使わず<code>item_list.append(tmp_item)</code>としてしまったら、1つの商品情報が複数入ったdictができてしまい、首をひねって日を跨ぐことに。</p> <p>以下の記事に助けられました。</p> <p><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/dogrunjp/9748789">Pythonのリストにdict型の変数をappendすると変数がポインタ的に振る舞うので… · GitHub</a></p> <p>この理屈は理解しておいた方が良さそうなので、また別途まとめたいですね。</p> <h3 id="(3)pandasでデータを整形"><a href="#%283%29pandas%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%95%B4%E5%BD%A2">(3)pandasでデータを整形</a></h3> <p>dict型のlistを作ることができたら、あとは難しいことはなく<code>pandas</code>の基本操作で事足ります。<br /> データフレームを作り、また活用しやすくするために少し整形しておきます。</p> <pre><code class="python"># データフレームを作成 item_df = pd.DataFrame(item_list) # 列の順番を入れ替える items_df = items_df.reindex(columns=['itemName', 'itemPrice', 'itemCaption', 'itemUrl', 'shopName', 'shopUrl']) # 列名を日本語語に変更する items_df.columns = ['商品名', '商品価格', '商品説明文', '商品URL', '店舗名', '店舗URL'] </code></pre> <h3 id="(4)csv出力"><a href="#%284%29csv%E5%87%BA%E5%8A%9B">(4)csv出力</a></h3> <p>作成したデータフレームをcsvファイルに出力します。</p> <pre><code class="python">items_df.to_csv('./rakuten_mayqueen.csv') </code></pre> <p><code>to_csv()</code>メソッドの引数には、保存先のパス(ディレクトリとファイル名)を指定します。今回は相対パスで、このスクリプトがあるディレクトリ直下にcsvファイルを作成しました。</p> <p>さて、出力されたデータをExcelなりSpreadSheetで開いてみましょう。</p> <p><a href="https://crieit.now.sh/upload_images/c3de87150b8d6a7b5ddc97919a9f2c545f4e48ce423c9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c3de87150b8d6a7b5ddc97919a9f2c545f4e48ce423c9.png?mw=700" alt="スクリーンショット 2020-09-01 20.28.15.png" /></a></p> <p>いい感じに取得することができました!</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p>ひとまず楽天市場から商品情報を取得して、csv出力まですることができました。<br /> 今後の方針としては、</p> <p><strong>(1)データの収集と整形</strong><br /> より目的にあったデータを必要な数だけ集めて、活用できる形に整形します。</p> <ul> <li>取得する商品数を増やす</li> <li>余計な商品を除外する</li> <li>重量(例えば商品名に「10kg」「5kg」などが入っている)を別カラムに切り出す</li> </ul> <p>単にデータ量を増やすだけでは意味がなくて、余計なデータや極端なデータを省いたデータを集める必要があります。商品情報取得時のソート順なども関係してきそうです。<br /> 例えば現状のデータだと、じゃがいもだけではなく包装資材やじゃがいもを使った別商品、謎に洋書まで取得してきている様子で、これらは除外するようにデータを加工しなければいけません。</p> <p><strong>(2)収集したデータの分析と意思決定</strong></p> <ul> <li>平均値、中央値、最小値、最大値などを算出してみる</li> <li>妥当な値決めを行う(意思決定)</li> </ul> <p>本当ならば別視点で「どの商品が売れ行きが良いのか」もデータを集めて分析できれば面白いのでしょうけど、今回はそこまでは考慮せず、現在の商品価格のみを見てみることにします。</p> <p>より正確な意思決定にはまた別の学習(プログラミングスキル以外の)が必要になってきますね。<br /> ただ、今回のデータを元に平均値などの統計量を見ることができれば、値決めの判断材料としては役に立つのかなーって思っています。<br />  <br /> そんな訳で、<a href="https://crieit.net/posts/IT-2-Python">次回</a>は少し込み入ったデータ収集と加工をしてみたいと思います。</p> <h2 id="(記事リンク)農家のITマーケティング試行錯誤日記"><a href="#%EF%BC%88%E8%A8%98%E4%BA%8B%E3%83%AA%E3%83%B3%E3%82%AF%EF%BC%89%E8%BE%B2%E5%AE%B6%E3%81%AEIT%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E8%A9%A6%E8%A1%8C%E9%8C%AF%E8%AA%A4%E6%97%A5%E8%A8%98">(記事リンク)農家のITマーケティング試行錯誤日記</a></h2> <p><a href="https://crieit.net/posts/Python-IT-API-csv">農家のITマーケティング試行錯誤日記1.楽天商品検索APIを使って商品情報をcsv出力する[Python]</a><br /> <a href="https://crieit.net/posts/IT-2-Python">農家のITマーケティング試行錯誤日記2.商品情報の収集とデータ加工を行う[Python]</a><br /> <a href="https://crieit.net/posts/IT-3-Python">農家のITマーケティング試行錯誤日記3.商品価格の集計と分析を行う[Python]</a></p> Massa