カテゴリー名: [ExcelVBAとピボットテーブル]
当シリーズでは、Excelのピボットテーブルをプログラミングのノウハウで生成するよう試みます。何回かに分けて掲載。
主に VBAの記述方法を見ていきます。
OLEを利用した場合の VBScript, JScript も掲載しますが、
その解説は要点のみとします。
当Webページで紹介するマクロファイルやVBScriptのファイル一式は、 vovPIVOT01.zip という圧縮ファイルに同梱しておきます。
Excelのピボットテーブルは、集計表を簡単に作るためのものです。
「性別、身長、体重」が400人分記録されているワークシートを元にして、
男女別に平均身長と平均体重を求めて表にする、といったことができます。
ピボットテーブルの話題に入る前に、前提となる事柄を少々。
マクロとスクリプトを実行した環境は次のとおり。
上記以外でも、Excelがインストールされていれば、たいていはマクロとスクリプトを動かせると思います。
Excel2007以降で動作するものをサンプルとして掲げますが、
必要に応じて Excel2003との違いに触れたいとおもいます。
SetExcelMacro_xlsm.vbs を実行すると、
ファイル選択ダイアログボックスが出るので、
macro01.txtなどのマクロファイルを選びます。
すると、そのマクロが組み込まれて Book1.xlsm というExcelファイルが作成されます。
また、マクロには Control + j
というショートカットキーが割り当てられます。
Book1.xlsm をExcelで開いてから Control + j
を入力するとマクロが実行されます。
SetExcelMacro_xlsm.vbsを利用する場合は
あらかじめExcelのマクロセキュリティを緩めておく必要があります。
ExcelをGUI操作して、次の設定変更を行います。具体的な操作方法は、Excelのバージョンによって違うのでここでは省略します。
なお、手作業によるマクロの組込みが苦にならないなら、SetExcelMacro_xlsm.vbsを使う必要はありません。
ソースデータとして pt_source.xls を用意しました。
中身は下のとおり。
ID | 性別 | 身長 | 体重 |
C3 | 女性 | 159.1 | 57.8 |
W5 | 男性 | 163.8 | 78.2 |
W11 | 女性 | 162.7 | 59.5 |
H1 | 女性 | 157 | 59.6 |
上の形式で Sheet1 に 400人分のデータが書かれています。
セル範囲でいえば A1:D401 にデータが書かれています。
このセル範囲には SourceDataRange という名前を付けてあります。
つまり、「名前付き範囲」になっています。
IDの列には空欄がありませんが、性別、身長、体重の列には少数ながら空欄があります。つまり、記載のない空白セルがあります。
ピボットテーブルの数値の集計(平均値、標準偏差あるいは個数の数え上げなど)では、空欄を「存在しないもの」としてスキップするようです。
下の表を作る手順をたどってみます。
値 | ||
行ラベル | 平均身長 | 平均体重 |
女性 | 160.099479 | 55.495263 |
男性 | 166.715657 | 66.446269 |
(空白) | 164.15 | 67.4 |
総計 | 163.46199 | 61.156743 |
「(空白)」のところは、「性別」が空欄の人の集計値です。
項目名を変更したくなるかもしれませんが、それは後回しにして、とりあえず上の表を作ります。
ピボットテーブルを作る手順を箇条書きにすると次のとおり。
マクロ全体を掲げると下のとおり。
マクロは Book1.xlsm に組み込まれており、
おなじフォルダに pt_source.xls があるとの前提です。
ピボットテーブルは、Book1.xlsm の第1ワークシートに作ります。
1Sub Macro1() 2 Dim pName As String, bName As String 3 Dim ptCache As PivotCache, ptObj As PivotTable 4 5 pName = ThisWorkbook.Path ' 本ワークブックのフォルダ名 6 bName = ThisWorkbook.Name ' 本ワークブックの名前 7 Workbooks.Open pName & "\pt_source.xls" ' ソースデータを開く 8 Workbooks(bName).Activate ' 本ワークブックをアクティブに 9 WorkSheets(1).Activate ' 第1シートをアクティブに 10 11 Set ptCache = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _ 12 SourceData:="[pt_source.xls]Sheet1!SourceDataRange") 13 Set ptObj = ptCache.CreatePivotTable( _ 14 TableDestination:="R1C1", TableName:="ピボット01") 15 16 ptObj.AddFields RowFields:="性別" 17 ptObj.AddDataField Field:=ptObj.PivotFields("身長"), _ 18 Caption:="平均身長", Function:=xlAverage 19 ptObj.AddDataField Field:=ptObj.PivotFields("体重"), _ 20 Caption:="平均体重", Function:=xlAverage 21End Sub
最初の方では、ソースデータが書かれた pt_source.xls を開いた上で、
Book1.xlsm を再びアクティブにしています。
ピボットテーブルに関する部分については以降で説明。
ピボットキャッシュは、ソースデータを取り込むための枠組みです。通常、ワークブックに所属します。
今回は Excelのワークブックをソースデータとして用いますが、Accessデータベースを利用することもできます。
どんな種類のソースデータを利用するのか、
また、どの範囲を利用するのかを決めてキャッシュを設定します。
該当のプログラム記述は下のとおり。
Set ptCache = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:="[pt_source.xls]Sheet1!SourceDataRange")
上の SourceType:=xlDatabase
がソースデータの種類の指定です。
種類として次の値を指定できます。
xlExternal, xlConsolidation については、いずれ触れたいとおもいます。
SourceData:="[pt_source.xls]Sheet1!SourceDataRange"
というのは、
ソースデータとして、どのワークブックの、どのワークシートの、どのセル範囲を利用するかを指定するものです。
もし Book1.xlsm の第1ワークシートにソースデータが書かれており、
第2シートにピボットテーブルを作るのであれば、
[pt_source.xls]
という記述は不要です。
SourceData:="Sheet1!SourceDataRange"
と書くだけで大丈夫です。
セル範囲は、R1C1:R401C4
のようにセル番地で指定することもできます。
SourceData:="Sheet1!R1C1:R401C4"
と書けます。
ただし、番地で指定するよりセル範囲の名前で指定する方が、柔軟性を持たせることになります。
ピボットテーブルは、更新(refresh)を簡単に行えます。
ソースデータに変更があれば、refreshによりピボットテーブルの集計結果もそれに対応して変わります。
その際、ソースデータの削除や追加によってセル範囲が変化してしまうと、
番地で指定している場合は意図したように更新されません。
セル範囲の名前で指定していれば、ソースデータ側で名前付けを調整する必要はありますが、ピボットテーブル側では単にrefreshするだけで適切に更新されます。
ピボットキャッシュからピボットテーブルオブジェクトを作ります。
一つのピボットキャッシュから複数のピボットテーブルオブジェクトを作ることができますが、まずは一つです。
テーブルの開始番地、テーブルの名前の二つを指定します。
Set ptObj = ptCache.CreatePivotTable( _
TableDestination:="R1C1", TableName:="ピボット01")
上の TableDestination:="R1C1"
は、アクティブシートの A1セルを開始位置にするとの指定です。
TableDestination:=WorkSheets(3).Range("A1")
とすれば、アクティブシートでなく第3シートの A1セルを開始位置にできます。
ただ、ピボットテーブルにいろいろな処理を施すなら、アクティブシートにピボットテーブルを設けるのが無難だとおもいます。
TableName:="ピボット01"
は、テーブルの名前を指定するものです。
省略してもかまいません。そのときは、「ピボット1」(半角カタカナ)のような適当な名前が割り当てられます。
複数のテーブルを設ける場合は、そのうちの一つを呼び出すのにテーブル名を手がかりにします。
なので、明示的にテーブル名を指定しておくのが無難かもしれません。
ピボットフィールドの指定は、テーブルの行と列をどんな形にするかの指定です。
今回の「性別」の内訳は、女性・男性・空白(記載無し)の三種類ですが、これを縦に並べるか横に並べるかを指定しなければなりません。
縦に並べるなら「行フィールド」にします。横に並べるときは「列フィールド」です。
マクロ記述としては下のとおり。
ptObj.AddFields RowFields:="性別"
上の RowFields
を ColumnFields
にすれば「列フィールド」になります。
一方、「身長」と「体重」は、その内訳による分類に使う訳ではありません。
「性別」とは違って、計算処理の手がかりにします。今回は平均値を算出します。
このような数値計算のフィールドは、AddFields でなく AddDataField で指定します。
ptObj.AddDataField Field:=ptObj.PivotFields("身長"), _
Caption:="平均身長", Function:=xlAverage
ptObj.AddDataField Field:=ptObj.PivotFields("体重"), _
Caption:="平均体重", Function:=xlAverage
上のようにすると、「平均身長」と「平均体重」が横方向に並べられます。
上の Function
に指定できるものとしては次のものがあります。
以上で、とりあえずピボットテーブルが形になります。
前述のマクロと同じ処理を行う VBScript を掲げます。
ワークブックにマクロを組み込む訳ではないので Book1.xlsx を生成します。
xlDatabase などの定数は、constants_xl.vbs を取り込むことで利用可能にしています。constants_xl.vbsは zip圧縮ファイルに同梱。
1Option Explicit 2Dim fso, bookPath, srcPath 3Dim exlApp, wbObj 4Dim ptCache, ptObj 5Include "constants_xl.vbs" 6 7Set fso = CreateObject("Scripting.FileSystemObject") 8bookPath = fso.GetAbsolutePathName("Book1.xlsx") 9If (fso.FileExists(bookPath) = True) Then fso.DeleteFile(bookPath) 10srcPath = fso.GetAbsolutePathName("pt_source.xls") 11Set exlApp = CreateObject("Excel.Application") ' Excelの起動 12exlApp.Visible = True ' Excelを見える状態に 13exlApp.Workbooks.Open srcPath 14Set wbObj = exlApp.Workbooks.Add() ' Workbookの新規作成 15wbObj.WorkSheets(1).Activate 16Set ptCache = wbObj.PivotCaches.Create(xlDatabase, _ 17 "[pt_source.xls]Sheet1!SourceDataRange") 18Set ptObj = ptCache.CreatePivotTable( _ 19 "R1C1", "ピボット01") 20 21ptObj.AddFields "性別" 22ptObj.AddDataField ptObj.PivotFields("身長"), "平均身長", xlAverage 23ptObj.AddDataField ptObj.PivotFields("体重"), "平均体重", xlAverage 24wbObj.SaveAs bookPath, xlOpenXMLWorkbook 25exlApp.quit 26 27Sub Include(ByVal FileName) 28 Dim fso, FileObj, MyStr 29 Set fso = CreateObject("Scripting.FileSystemObject") 30 Set FileObj = fso.OpenTextFile(fso.GetAbsolutePathName(FileName)) 31 MyStr = FileObj.ReadAll() 32 FileObj.Close 33 Set fso = Nothing 34 Set FileObj = Nothing 35 ExecuteGlobal MyStr 36End Sub
VBAのマクロでは ptObj.AddFields RowFields:="性別"
のように
パラメータ名の RowFields
を指定できますが、VBScript ではできません。
パラメータを規定の順番に即して配置することで対応します。
AddFields(RowFields, ColumnFields, PageFields, AddToTable)
というのが規定なので、今回は第1パラメータとして "性別"
を置きました。
ColumnFields
にしたければ PTobj.AddFields ,"性別"
と書きます。
前述のマクロと同じ処理を行う JScript を掲げます。
Book1.xlsx を生成します。
xlDatabase などの定数は、constants_xl.js を取り込むことで利用可能にしています。
1var fso, bookPath, srcPath; 2var exlApp, wb; 3var ptCache, ptObj; 4eval(ReadFile("constants_xl.js")); 5 6fso = WScript.CreateObject("Scripting.FileSystemObject"); 7bookPath = fso.GetAbsolutePathName("Book1.xlsx"); 8if (fso.FileExists(bookPath)) fso.DeleteFile(bookPath); 9srcPath = fso.GetAbsolutePathName("pt_source.xls"); 10exlApp = WScript.CreateObject("Excel.Application"); // Excelの起動 11exlApp.Visible = true; // Excelを見える状態に 12exlApp.Workbooks.Open(srcPath); 13wb = exlApp.Workbooks.Add(); // Workbookの新規作成 14wb.WorkSheets(1).Activate(); 15 16ptCache = wb.PivotCaches().Create(xlDatabase, 17 "[pt_source.xls]Sheet1!SourceDataRange"); 18ptObj = ptCache.CreatePivotTable( 19 "R1C1", "ピボット01"); 20 21ptObj.AddFields("性別"); 22ptObj.AddDataField(ptObj.PivotFields("身長"), "平均身長", xlAverage); 23ptObj.AddDataField(ptObj.PivotFields("体重"), "平均体重", xlAverage); 24wb.SaveAs(bookPath, xlOpenXMLWorkbook); 25exlApp.Quit(); 26 27function ReadFile(filename) { 28 var fso = WScript.CreateObject("Scripting.FileSystemObject"); 29 var path = fso.GetAbsolutePathName(filename); 30 var MyStr = null; 31 if (fso.FileExists(path)) { 32 var fobj = fso.OpenTextFile(path, 1); 33 MyStr = fobj.ReadAll(); 34 fobj.Close(); 35 } 36 return MyStr; 37}
JScriptを書く際につまずいたのは下の箇所です。
ptCache = wb.PivotCaches().Create(xlDatabase,
"[pt_source.xls]Sheet1!SourceDataRange");
PivotCaches().Create ……
で、PivotCaches
の後に括弧がないとエラーになるので要注意です。
今回、「身長」と「体重」は数値計算要のフィールドとして利用しました。
つまり、データフィールドに指定しました。
結果、表の中で「平均身長」と「平均体重」が横方向に配置されました。
では、「性別」を行フィールド(縦方向に並べる)でなく、列フィールドにした場合はどうなるでしょうか。
Excel2003でピボットテーブルを設定したことのある人なら、
「平均身長」と「平均体重」が自動的に縦に配置されることを知っているでしょう。
つまり、下のような表になります。
データ | 女性 | 男性 | (空白) | 総計 |
平均身長 | 160.099479 | 166.715657 | 164.15 | 163.46199 |
平均体重 | 55.495263 | 66.446269 | 67.4 | 61.156743 |
しかし、Excel2007以降ではそうなりません。
「平均身長」と「平均体重」は、相変わらず横方向に配置されます。
下のような表になります(数値は短縮して表示)。
女性 | 男性 | (空白) | 全体の 平均身長 | 全体の 平均体重 | |||
平均身長 | 平均体重 | 平均身長 | 平均体重 | 平均身長 | 平均体重 | ||
160.1 | 55.5 | 166.7 | 66.4 | 164.2 | 67.4 | 163.5 | 61.2 |
これを Excel2003 の場合と同じように配置するにはどうするかですが、
データフィールドの設定が一通り済んでから次ぎの1行を置きます。
ptObj.DataPivotField.Orientation = xlRowField
そうすると、データフィールドが行フィールドとして縦方向に配置されます。
あらためて該当箇所を示すと次のとおり。
ptObj.AddFields ColumnFields:="性別"
ptObj.AddDataField Field:=ptObj.PivotFields("身長"), _
Caption:="平均身長", Function:=xlAverage
ptObj.AddDataField Field:=ptObj.PivotFields("体重"), _
Caption:="平均体重", Function:=xlAverage
ptObj.DataPivotField.Orientation = xlRowField
本筋からは脱線ですが、Excel2003バージョンのピボットテーブルを作成すれば、
DataPivotField.Orientation
を指定しなくても「平均身長」などが縦に並びます。
ピボットキャッシュを作成するとき、第3のパラメータ Version を指定することで Excel2003 のピボットテーブルにすることができます。
具体的には次のとおり。
Set ptCache = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:="[pt_source.xls]Sheet1!SourceDataRange", _
Version:=xlPivotTableVersion11)
バージョンとして指定できるのは下の定数です。
xlPivotTableVersion2000 | 0 | Excel2000 |
xlPivotTableVersion10 | 1 | Excel2002 |
xlPivotTableVersion11 | 2 | Excel2003 |
xlPivotTableVersion12 | 3 | Excel2007(default) |
xlPivotTableVersion14 | 4 | Excel2010 |
xlPivotTableVersion15 | 5 | Excel2013 |
あるいは、ピボットキャッシュを作るとき、Create でなく Add を使えば、バージョンを指定しなくても Excel2003バージョンになるようです。
Set ptCache = ActiveWorkbook.PivotCaches.Add(SourceType:=xlDatabase, _
SourceData:="[pt_source.xls]Sheet1!SourceDataRange")
上のようにすれば Excel2003バージョンになります。
〜 以上 〜
追伸
zip圧縮ファイルには macro01(基本形).txt, macro02(データフィールドの配置変更).txt, macro03(Excel2003バージョンで作成).txt が入っています。
また、それぞれについて同じように動作する VBScript, JScript も含まれています。