古庄 潤(ふるしょう じゅん)

本業はエンジニア。ICに様々な機械をつなぎ,電流やら電圧を測定する。もちろん,これらの測定器もVBAでコントロールし,取り込んだデータもマクロで処理する。人呼んで,マクロの鬼軍曹!

診断(6)
グラフを一括して変換する

「あ~あ(泣)」
「おや? どうしちゃったんですか」
「実は,お気に入りのウキが折れたんじゃ。もうだめじゃ」
「先生,何を言ってるんですか!『人間万事塞翁が馬』。また,いいウキに出会えますよ」
「おぉ,ゲール君,よくそんな言葉を知っているな」
「先生と違って日々精進していますからね」
「……」
「次の方,ど~ぞ~」

今月の相談
「作成した表とグラフを,上司から『折れ線グラフではなく,散布図に変えてくれ』と言われました。表と折れ線グラフのファイルは複数あるので,マクロで一括処理しようとしましたが,リスト1のコードでエラーになりました(図1)。どこが間違っているのでしょうか?」


Sub GRAPH_HENKOU()
  ThisWorkbook.Worksheets(1).Charts(1).ChartType = xlXYScatter
End Sub
リスト1●エラーになるコード

図1●エラー・メッセージ
図1●エラー・メッセージ

「ふむふむ,これはよくある間違いじゃな」
「そうなんですか? 私には,正しいコードに見えますが?」
「うむ,Chartsプロパティを使っておるじゃろう」
「そうですね。作成したグラフは,Chartsオブジェクトに含まれるんですよね」
「そこじゃ! そこに落とし穴がある」
「どういう意味ですか?」
「Excelで作成するグラフには,グラフ・シートとグラフ・オブジェクトがあるのを知っているかな?」
「あ,そうでした! 2種類のグラフがありました」
「そう,Chartsプロパティで取得するChartsオブジェクトに含まれているのは,グラフ・シートなんじゃよ。相談者が作成したのはグラフ・オブジェクト。だからエラーになる」
「なるほど。でも,エラー・メッセージが変じゃありませんか?」
「うむ,ゲール君も鋭くなったな。エラーの原因は“Worksheets(1).Charts(1)”の部分じゃ」
「どういうことですか?」
「Worksheetオブジェクトは,Chartsオブジェクトの親オブジェクトではないのじゃ。つまり,エラー・メッセージを通訳すると『Worksheetオブジェクトは,Chartsプロパティをサポートしていません』となる」
「あ,そうか,両方ともシート・オブジェクトですもんね」
「ん~,そういうことでもないんじゃが,まぁ,よかろう。Chartsオブジェクトの親オブジェクトはWorkbookオブジェクトじゃ」
「じゃあ,これでいいんですか?」


Sub TEST_GRAPH_HENKOU()
  ThisWorkbook.Charts(1).ChartType = xlXYScatter
End Sub
リスト2●やっぱりエラーになるマクロ

図2●それでも出るエラー・メッセージ
図2●それでも出るエラー・メッセージ

「あれ? 先生,やっぱりエラーになりますよ」
「うむ。それは,ThisWorkbookにグラフ・シートが無いからじゃ」
「なるほどねぇ,じゃ,そろそろ解決策を教えてください」
「それじゃ,簡単な例として,グラフ・オブジェクトの名前を確認してみようかのう」


Sub GRAPH_NAME()
  MsgBox ThisWorkbook.Worksheets("SAMPLE").ChartObjects(1).Name
End Sub
リスト3●グラフ・オブジェクトの名前を表示するマクロ

「ゲール君,ポイントは,ChartObjectsプロパティじゃ。グラフ・オブジェクトを取得する場合は,ChartObjectsプロパティを使う。覚えておきたまえ」
「ははっ!肝に銘じて」

 では,本番のマクロを紹介しましょう。図3は変更前のグラフです。折れ線グラフで,グラフのタイトルや,軸線のタイトル/単位は表示されていません。グラフの上にある表と一緒に見れば,内容は一目瞭然ですが,ついでですからグラフや軸線のタイトルも追加しましょう。リスト4を見てください。

図3●元のグラフ
図3●元のグラフ
[画像のクリックで拡大表示]

リスト4●ユーザーが指定したファイルの折れ線グラフを,散布図に変換するマクロ。複数のファイルを指定できる
リスト4●ユーザーが指定したファイルの折れ線グラフを,散布図に変換するマクロ。複数のファイルを指定できる

 最初にGetOpenFilenameメソッドを使って,ユーザーにファイルを選択させます(1)。このとき,複数のファイルを一括して処理できるようにMultiSelectオプションをTrueにします。戻り値は,Variant型の変数myBOOK_NAMEに代入します。ユーザーが一つ以上のファイルを選択すると,変数myBOOK_NAMEは配列変数となります。

 そこで(2)で,IsArray関数で変数myBOOK_NAMEが配列であるかどうかを調べます。(1)のステートメントで,ユーザーが[キャンセル]ボタンをクリックした場合は,IsArray関数の戻り値がFalseとなり,(3)のステートメントでサブルーチンを抜け,マクロは終了します。

 ファイルが選択されている場合は,変数myBOOK_NAMEの要素(ファイル)の数だけ繰り返すループに進みます(4)。LBound関数で配列の添え字の最小値を,UBound関数で最大値を取得することで,ループの回数を制御しています。また,その回数「i」は,順次ファイルを開く(5)のステートメントで,配列の添え字として使っています。

 元の折れ線グラフで,X軸の項目にしていたセルC4からE4の値は散布図では使えないので,Val関数を使って数値だけを取り出して,元のセルの値を置換します(6)。

 (7)では,Withステートメントで,グラフのオブジェクトやプロパティを操作するコードをシンプルに書けるようにしています。ただし,間違いやすい落とし穴があるので,注意してください。実は,ChartObjectsオブジェクトはグラフの入れ物にすぎず,グラフの実体はChartオブジェクトにあります。つまり,グラフ関連のオブジェクトやプロパティはChartオブジェクトにあるので,間違えないようにしてください。

 (8)で,グラフの種類を散布図に変更します。グラフ・タイトルの表示をイネーブルにし(9),タイトルに表示する文字列を指定します(10)。

 次に,Y軸タイトルの表示をイネーブルにし(11),そこに表示する文字列を指定します(12)。同様に,X軸タイトルの表示をイネーブルにし(13),その文字列を指定します(14)。

 それを終えたら,グラフを変更したブックを保存して閉じます(15)。また,ループを抜けたら,ワークブックを代入していた作業用のオブジェクト型の変数を空にして,メモリーを開放します(16)。

 では,早速実行してみましょう(図4)。

図4●変更後のグラフ
図4●変更後のグラフ
[画像のクリックで拡大表示]

「先生,お見事です! 折れ線グラフが散布図に変わりました」
「うむ,苦しゅうない。近う寄れ」
「それは遠慮します。ところで,どうして折れ線グラフを散布図に変更したんでしょうね」
「そうじゃな,相談者の上司が何で散布図に変更させたかは想像するしかない。おそらく,温度による抵抗値の変化のリニアリティを見たかったのじゃろう。折れ線グラフでは,等間隔でデータを並べないとリニアリティは表現できない。今回のデータは,0℃,25℃,100℃と間隔がバラバラじゃから,このまま折れ線グラフにすると正しい傾きが見えない」
「へぇ~,奥が深いんですね」
「うむ,わしの愛と一緒じゃ」
「……」