Help us understand the problem. What is going on with this article?

PowerBIのPythonビジュアルをとりあえず使ってみた

改訂版 はじめに

最初にこの記事を発表し,1年半がたちました。本日(2020/2/29)行われた,Power BI 勉強会 @ 東京 #16 において,この記事に対する質問があり,過去作成したpbixファイルを見てみたところ,指摘の通りビジュアルがすべて表示されていませんでした。原因は,Pythonの環境が当時のものと違っており,必要なパッケージが組み込まれていなかったためでした。そこで,その点について,記述を追加しました。

はじめに

2018年8月のアップデートからPowerBIでPythonがプレビュー機能ですが使えるようになりました。
Power BI Desktop August 2018 Feature Summary

現在使える場面は,

  • データを取得する際にPythonスクリプトを使ってデータフレームを作成してこれをデータモデルにインポートすること
  • クエリエディタでクエリをpythonスクリプトを使って加工すること
  • PowerBIのデータを使ってPythonでグラフなどのビジュアルを作ってレポートに表示させること

の3つです。(2018/9/11抜けていたので追加)

このうち,1つ目のデータ取込みについてはPowerBIにPythonを導入することを含めて,
Python × PowerBI データ分析事始め【データ準備編】
を見ていただくとして,ここでは,3つ目のPythonビジュアルを使うにあたって,つまづいたことも含めてご紹介したいと思います。若干コードが素人っぽいですが,その辺は大目にみてください・・・・。(2つ目は重要だけどスルー)

Python ビジュアルを行う前に,やっておくこと(2020/2/29追記)

Power BI Desktop で Python スクリプトや Python ビジュアルを実行する際には,以下の準備が必要です。(ゼロから始めるとして)

  • Python をPCにインストールする(まだインストールしていない場合)
  • 必要な Python パッケージをインストールする
  • Power BI Desktop で Python スクリプトを有効にする

これらの手順に関しては,Power BI Desktop で Python スクリプトを実行する をご覧ください。

なお,この記事にあるスクリプトを実行する際には,pandas, matplotlib, sklearn, numpy のパッケージをインストールしておいてください。

とりあえずグラフを書いてみる

データを用意する

(2020/5/24データの出典を訂正)
今回利用するデータはKaggleで公開されているdatasetの"house sale prediction"にあるhousing.csvを使用します。これをあらかじめPowerBIに取込んで,データを10進数型に設定しておきます。このPowerBI上のデータを使ってPythonで作図しようというのが,今回のメインネタです。
image.png

Pythonビジュアルの配置

pythonのmatplotlibを使ってsqrt_livingpriceの散布図を作ってみます。
まず,視覚化ウインドウにある「Pythonビジュアル」アイコン(Pyマーク)をクリックすると,キャンバスに図のようなpythonマークの入ったウインドウと「Pythonスクリプトエディター」が表示されます。
image.png
次に,フィールドウインドウの中から,作図に使うsqrt_livingpriceを,視覚化ウインドウの「値」の所へドラッグします。(それぞれのフィールドは集計しないように設定しておきます。)
すると,「Pythonスクリプトエディター」に薄いグレーの背景色で次のコードが自動的に生成されます。

# Create dataframe
# dataset = pandas.DataFrame(sqft_living,price)

# Remove duplicated rows
# dataset = dataset.drop_duplicates()

これは,sqrt_livingpriceの2つのフィールドの値をもったdatasetという名前のデータフレームを作成して,このデータフレームから,2つのフィールドの値が重複する行を削除するということを表しています。以降,Pythonスクリプトではこのdatasetデータフレームにアクセスして,作図を行っていきます。
image.png

Pythonスクリプトの挿入

ここまで準備ができたら,あとはグラフ(散布図)を描くPythonのコードをPaste or type your script code hereと書いてあるところに挿入します。書いてある通りに,type~直接コードを打ち込んでも,paste~他の開発環境で作成したコードをコピペしても,どちらでも構いません。ここでは以下のコードを挿入します。

import matplotlib.pyplot as plt
plt.plot(dataset['sqft_living'], dataset['price'], ".")
plt.xlabel("sqft_living", size = 15)
plt.ylabel("price", size=15)
plt.show()

挿入したら,「Pythonスクリプトエディター」の上部右側にある「スクリプトの実行」アイコン(一番左)をクリックすると,スクリプトが実行され,グラフが表示されます。
このグラフはスクリプトにより作成されているので,PowerBIに組み込まれているグラフと違って,インタラクティブな機能はありません(グラフの図形をマウスでクリックしても値は出てきません)。
image.png
ここで,値にフィルターをかけると,それに従ってグラフも自動的に書き換わります。この際,コードの書き換えは必要ありません。
なお。値のフィールドを変更した場合は,データフレームの定義が変わりますので,コードの書き換えが必要となる場合があります。
image.png

せっかくなので,Pythonで処理した結果を作図する

機械学習で回帰分析

多少Pythonぽいということで,sklearnを使ったsqrt_living=家の面積からprice=売却価格を予測する回帰分析の結果を図化してみます。
理論的なところは飛ばしますが,以下のコードで線形(1次)回帰モデル,2次多項式回帰モデルを作成し回帰分析を行います。なお,このモデルの場合は全データを使ってモデルを作ってもいいのですが,教師データとして先頭から3/4までのデータを用いてモデルを作ることにしました。

from sklearn.linear_model import Lasso
from sklearn.preprocessing import PolynomialFeatures 
import numpy as np

n_train = len(dataset) * 3 // 4         # 学習に使うデータ数を全データ数の3/4程度に設定
train_X = dataset["sqft_living"][0:n_train].reshape(-1,1)   # 説明変数(sqft_living=家の面積)
train_Y = dataset["price"][0:n_train]                       # 目的変数(price=売却価格)

#線形回帰モデルの作成. 
rgs1 = Lasso()
rgs1.fit(train_X, train_Y)                         # 家の面積, 売却価格の関係を学習(線形回帰)
# 学習済みのlassoモデルを用いて家の大きさ0~10000の時の売却価格を予測
X = np.arange(10000)
Y = rgs1.predict(X.reshape(10000,1))               # 家の面積0~10000の時の売却価格を予測

#2次式の多項式回帰モデルの作成. 
rgs2 = Lasso()
quadratic_train = PolynomialFeatures(degree = 2)
X_quadratic_train = quadratic_train.fit_transform(train_X)   # 説明変数xについてx,x**2の項を作成 
rgs2.fit(X_quadratic_train, train_Y)                         # 家の面積, 売却価格の関係を学習
a1, a2 = rgs2.coef_[1:]   # 係数を取得
b = rgs2.intercept_       # 切片を取得

X = np.arange(10000)
Y_quadratic = a1*X + a2*X**2 + b                     # 家の面積0~10000の時の売却価格を予測

予測結果をプロットする

予測結果(線形回帰モデル=X~Y,2次多項式モデル=X~Y_quadratic)を,教師データtrain_X~train_Yとともに散布図にプロットします。コードは以下のとおりです。PowerBIには,上のコードと下のコードを合体したものをPythonスクリプトとして先の方法で挿入します。

import matplotlib.pyplot as plt
plt.plot(y, label = "First-order equation")
plt.plot(y_quadratic, label = "Quadratic equation")
plt.plot(y_cubic, label = "Cubic equation")
plt.scatter(X_test,  y_test,  s=1, c ="r", label = "price_sqftliving")
plt.xlabel("sqft_living" ,size = 15)
plt.ylabel("price", size=15)
plt.xlim(0, 10000)
plt.legend()
plt.show()

で,作図結果は下の図の右のグラフになります。ここで,グラフをよく見ると,教師データ(赤色の点)がpriceがおおよそ600000以下のデータのみ表示されています。教師データとしてデータの先頭から3/4の数のデータを使っただけなのに,priceの小さい順に3/4の数を扱ったことになっています。これは,おそらくPython側にデータを渡すときにデータがソートされてから渡されているものと推測されます。このあたりを認識しないといろいろと間違えそうなので,要注意です。
image.png

では,どうなっているのか

データの順番を見るために,次のコードでグラフを書いてみます。横軸に0からデータ数までの整数,縦軸にsqrt_livingpriceのそれぞれの値をプロットした散布図です。

import matplotlib.pyplot as plt
plt.subplot(2, 1, 1)
plt.title('data_length=' + str(len(dataset)), size=15)
plt.plot(range(0,len(dataset)),dataset['sqft_living'], '.')
plt.ylabel("sqft_living", size=15)
plt.subplot(2, 1, 2)
plt.plot(range(0,len(dataset)),dataset['price'], '.')
plt.ylabel("price", size=15)
plt.show()

あらためて,「値」のところにsqrt_livingpriceのフィールドをドラックした直後のグラフは下の図のとおりです。値を配置した順番はsqrt_livingpriceの順で,ドラックしただけで何の設定もしてません。したがって,2つのフィールドの合計値しかデータを渡していません。
image.png
次にsqrt_livingの集計方法を「集計しない」に変更した直後のグラフは下の図のようになります。sqrt_livingの値ごとにpriceが集計(合計)された上で,sqrt_livingの値でソートされたデータが渡されています。sqrt_livingの一意の値の個数が405なので,データ数も405となっています。
image.png
さらにpriceの集計方法を「集計しない」変更すると,sqrt_livingpriceの生データがsqrt_livingでソートされたものが渡され,そのうちsqrt_livingpriceのペアの値が重複するものが削除されます。重複が削除されたのでデータ数は1980になります。(元のデータ数は2000)
image.png
ちなみに,上の手順で先にpriceの集計方法を「集計しない」に変更した直後のグラフは下の図のようになります。このとき,sqrt_livingは大きい順にソートされます。さらに,sqrt_livingの集計方法を「集計しない」に変更すると,上の手順の結果と同じになります。(sqrt_livingは小さい順にソートされます)
image.png

さらに追ってみると・・・・からの対応策

こんどは,「値」のところに配置する順番をpricesqrt_livingとした場合を見ていきます。ドラックした直後は両フィールドともに集計方法が合計なので値は1つ渡しになります。(上記と同じ)
次にpriceの集計方法を「集計しない」に変更した直後のグラフは下の図のようになります。priceの値ごとにsqrt_livingが集計(合計)された上で,priceの値でソートされたデータが渡されています。priceの一意の値の個数が906なので,データ数も906となっています。
image.png
さらにsqrt_livingの集計方法を「集計しない」変更すると,sqrt_livingpriceの生データがpriceでソートされたものが渡され,そのうちsqrt_livingpriceのペアの値が重複するものが削除されます。重複が削除されたのでデータ数は1980になります。(元のデータ数は2000)
image.png
(先にsqrt_livingの集計方法を「集計しない」変更した場合のグラフは省略。)

以上のことから,「値」にフィールドを配置する順番でソート順がかわること,変数の組合せが重複するとデータが削除されることがわかります。であれば,あらかじめデータの入力順にソートされ,かつ一意の値を持ったフィールドを追加してこれを最初に「値」に配置してやれば,データ本体がソートされずにPython側に渡すことができるはずです。そのような追加するフィールドといえば,元のデータにインデックス列を追加すればよいです。
image.png
インデックス列を最初に「値」に配置して,集計方法を「集計しない」変更しておけば,そのあと追加するフィールドは,重複しない値を持ったインデックス列に基づいて集計されることになるので,結局指定したフィールドの生データがそのまま渡されることになります。
image.png
このフィールドの設定方法を先ほどの回帰分析のグラフに適用してグラフを描くと下の図のようになります。左側の生データの散布図と,右側の教師データの散布図がほぼ似たような分布になりました。といえど,教師データは先頭から3/4の数のデータなので,生データの散布図とは完全に一致しません。
image.png

まとめ

くどくど説明してきましたが,PythonビジュアルでPython側に渡るデータセットは,Pythonのスクリプト内でどう扱うかを考えて設定しないと痛い目に合うはずです。気をつけましょう。

(2020/2/29追記)Power BI Desktop の Python スクリプト,Python ビジュアルは現時点で GA となっています。なので,いろいろ使ってみたいところですが,とにかく動作が重いんですw。
Power BI において Python スクリプトの使いどころは,よく吟味してから行ってください。(他の方法があれば,ぜひ検討してみてください。)

それでは,ここまでお付き合いいただき,ありがとうございます。


【2018/08/25追記】

PowerBIのPythonビジュアルについては,こちらもぜひどうぞ!

Python × PowerBI データ分析事始め【ビジュアル編】


【2020/02/29追記】

これらの記事も参考にしてください。
* Python visualizations in Power BI Service
* Python を使用して Power BI ビジュアルを作成する
* PowerBIでseabornのpairplotを見たい

ryoukom1216
野良データ分析者
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした