VBA(Excel)でグラフのデータ範囲の取得
ExcelのVBAでのチャートの操作について、次のようなことがしたい場合があります。
1.チャートに別のデータ範囲を設定したい
2.チャートのデータ範囲を取得したい
1.のチャートに別のデータ範囲を設定したい場合は、
ActiveChart.SetSourceData Source:=Range(cells(1,1), Cells(20,3)
のような感じで設定できます。ネットで検索するとこれについての記事はたくさんあります。
2.のチャートのデータ範囲を取得したい場合は、良い方法がないのか、あまり知られていません。あまりスマートなやり方ではありませんが以下に作成したVBAを示します。
チャートに設定されているデータ範囲は、チャートオブジェクトのシリーズコレクションのフォーミュラプロパティー(Chart.SeriesCollection.Formula)にあり、その値は例えば =SERIES(Sheet1!$D$3,Sheet1!$B$10:$B$20,Sheet1!$D$10:$D$20,2)
のような形式の文字列です。
ここから$D$3や$B$10:$B$20を検索して取り出すことにします。検索は正規表現を用いました。ヒットする件数はチャートによるので結果はDictionaryに入れて返します。
ソースコード
1.チャートに別のデータ範囲を設定したい
シートに設定したい行を入力するセルを作り、その値を読み取ってチャートの範囲を変更します。
Public Sub データの範囲を設定する()
'開始行、終了行を取得する
Dim rowS As Integer
Dim rowE As Integer
rowS = Cells(17, "G")
rowE = Cells(18, "G")
'チャートの範囲を決定する
'タイトルの範囲:Range(Cells(3, "B"), Cells(3, "D"))
'データの範囲:Range(Cells(rowS, "B"), Cells(rowE, "D"))
Dim rng As Range
Set rng = Union(Range(Cells(3, "B"), Cells(3, "D")), Range(Cells(rowS, "B"), Cells(rowE, "D")))
'チャートに範囲を設定する
Dim cht As Chart
Set cht = ActiveSheet.ChartObjects(1).Chart 'アクティブなシートにある1つ目のチャート
cht.SetSourceData Source:=rng
End Sub
2.チャートのデータ範囲を取得したい
アクティブなシートにある1つ目のチャートを対象にして、シリーズコレクションを取得し、そのフォーミュラからアドレス文字を検索をし、Dictionaryに保存します。
アドレスは単独セルアドレスと範囲アドレスに分けて検索します。
Dictionaryキーは、通し番号-単独/範囲番号-連番 としてあります(深い意味はありません)。
Public Sub データの範囲を取得する()
Dim cht As Chart
Set cht = ActiveSheet.ChartObjects(1).Chart 'アクティブなシートにある1つ目のチャート
' Set cht = ActiveChart'アクティブなチャート
'チャートのシリーズコレクション
Dim sc As SeriesCollection
Set sc = cht.SeriesCollection
'設定されている内容
Dim scf As Variant
scf = GetSCFormula(sc)
'アドレスを取得
Dim dic As Dictionary
Set dic = GetAddress(scf)
'アドレス取得結果を表示
Dim msg As String
msg = ""
Dim keywd As Variant
For Each keywd In dic.Keys
msg = msg & keywd & " : " & dic(keywd) & vbCrLf
Next
Dim uf As New UserForm1
uf.TextBox1.Text = msg
uf.Show
'=Sheet1!$B$3:$D$3,Sheet1!$B$10:$D$20
End Sub
'SeriesCollectionに設定されている内容
Private Function GetSCFormula(sc As SeriesCollection) As Variant
Dim scf As Variant
ReDim scf(sc.Count)
Dim i As Integer
For i = 1 To sc.Count
scf(i) = sc(i).Formula
Next
GetSCFormula = scf
End Function
Private Function GetAddress(scf As Variant) As Dictionary
Dim re As New RegExp '正規表現で検索する
Dim mchs As MatchCollection '正規表現の検索結果
Dim dic As New Dictionary '結果を保存する辞書
With re
.Global = True
Dim iscf As Integer
Dim imchs As Integer
Dim isub As Integer
Dim keywd As Variant
For iscf = 1 To UBound(scf)
'単独のアドレスを取得する
.Pattern = "!(\$[A-Z]+?\$[0-9]+?)," '単独のアドレス
Set mchs = .Execute(scf(iscf))
If mchs.Count > 0 Then
For imchs = 0 To mchs.Count - 1
For isub = 0 To mchs(imchs).SubMatches.Count - 1
keywd = dic.Count & "-" & imchs & "-" & isub
dic(keywd) = mchs(imchs).SubMatches(isub)
Next
Next
End If
'範囲のアドレスを取得する
.Pattern = "(\$[A-Z]+?\$[0-9]+?:\$[A-Z]+?\$[0-9]+?)," '範囲のアドレス
Set mchs = .Execute(scf(iscf))
If mchs.Count > 0 Then
For imchs = 0 To mchs.Count - 1
For isub = 0 To mchs(imchs).SubMatches.Count - 1
keywd = dic.Count & "-" & imchs & "-" & isub
dic(keywd) = mchs(imchs).SubMatches(isub)
Next
Next
End If
Next
End With
Set GetAddress = dic
End Function
シリーズコレクションのフォーミュラが以下であれば
=SERIES(Sheet1!$D$3,Sheet1!$B$10:$B$20,Sheet1!$D$10:$D$20,2)
=SERIES(Sheet1!$C$3,Sheet1!$B$10:$B$20,Sheet1!$C$10:$C$20,1)
次のような結果が得られます。
0-0-0 : $C$3
1-0-0 : $B$10:$B$20
2-1-0 : $C$10:$C$20
3-0-0 : $D$3
4-0-0 : $B$10:$B$20
5-1-0 : $D$10:$D$20
なお、正規表現と連想記憶を使うために、次の二つを参照設定しています。
Microsoft VBScript Reguler Expression 5.5
Microsoft Scripting Runtime
作成したExcelは以下です。
#Excel , #VBA , #Chart , #SeriesCollection
応援してやろうということで、お気持ちをいただければ嬉しいです。もっと勉強したり、調べたりする糧にしたいと思います。