VBAからGDI+を使う資料集
inet上の達人の方々から御教示いただいたコードを少しアレンジさせてもらっています(切り貼りとも言います)。
  1. ホーム
  2. Other
  3. specialgraphs


Excelの散布図をアレンジする

散布図をVBAで制御すると、一見散布図には思えないようなグラフを作成する事ができます。その様な事例を中心にグラフ関係のコードをまとめてみました。

  1. マーカーにセル画像(emf)を貼り付ける
  2. 散布図上でPoint間を矢印で結び、点間のベクトルを表示
  3. Y軸目盛りの代わりに文字を表示する(ダミー列のデータラベル応用)
  4. 異なる列に存在するデータを1系列に結合する
  5. Recordsetから直接グラフを作成(エクセル)
  6. グラフに基準線を引く(基準線用データを用いない)
  7. 棒グラフや折れ線グラフでY軸の数値を別の値で置き換える
  8. グラフの名前でソートして並び替える
  9. メモリー上で集計して円グラフを描く
  10. 散布図上に桁数が異なる折れ線グラフをプロットする援用(Y軸データラベル生成)
  11. Offsetをかけた補助系列を利用して、吹き出し等でPointにコメントをつける
コンテンツリストに戻る


'===========================================================================
'☆マーカーにセル(複数)を画像としてコピーしたものを設定する
'X軸に張り付いた作業用系列を設定し、数値の代わりにデータラベルで文字を表示する
'データが入っているセル(+隣のセル)を画像としてコピーしたものをマーカーに貼り付ける

Sub test()
  Dim sh As Worksheet
  Dim myChart As Chart
  Dim dataRange As Range, labelRange As Range
  Dim i As Long, chartObjCount As Long
  
  'データラベル表示のオフセット値
  Const dataLabelOffsetX As Double = -25
  Const datalabelOffsetY As Double = 10
  'プロットエリアのオフセット値(必要によりデータラベル分の余白を作成)
  Const plotAreaResizeX As Double = 0
  Const plotAreaResizeY As Double = 20
  'グラフのサイズ
  Const graphWidth As Double = 600
  Const graphHeight As Double = 400
    
  Set sh = ThisWorkbook.Worksheets(1)
  With sh
    'データのセル範囲を設定
    Set dataRange = .Range(.Range("B2"), .Range("B" & .Rows.Count).End(xlUp))
    Set dataRange = dataRange.Offset(0, -1).Resize(, 4)
    'ダミー系列のセル範囲を設定、ダミー系列はラベル表示のみに使用する
    Set labelRange = .Range(.Range("G2"), .Range("G" & .Rows.Count).End(xlUp))
    Set labelRange = labelRange.Offset(0, -1).Resize(, 3)
  End With
  'グラフの作成(データ系列は後で設定)
  chartObjCount = sh.ChartObjects.Count
  Set myChart = Charts.Add
  myChart.Location Where:=xlLocationAsObject, Name:=sh.Name
  With sh.ChartObjects(chartObjCount + 1)
  Set myChart = .Chart
  .Width = graphWidth
  .Height = graphHeight
  End With
  '散布図
  myChart.ChartType = xlXYScatter
  
  With myChart
    .SeriesCollection.NewSeries
    'ここで列の見出し文字をデータ中に含めてしまうと、意図したグラフの表示がなされない。要注意。
    .SeriesCollection(1).XValues = dataRange.Columns(2)
    .SeriesCollection(1).Values = dataRange.Columns(4)
    .SeriesCollection(1).Name = dataRange.Columns(2).Cells(1).Item(0).Value
    .SeriesCollection.NewSeries
    .SeriesCollection(2).XValues = labelRange.Columns(2)
    .SeriesCollection(2).Values = labelRange.Columns(3)
    .SeriesCollection(2).Name = labelRange.Columns(2).Cells(1).Item(0).Value
    .SeriesCollection(2).MarkerStyle = -4142
    .HasLegend = False
    'データラベル移動の余地を準備
    .PlotArea.Height = .PlotArea.Height - plotAreaResizeY
  End With
  myChart.PlotArea.Border.Color = vbBlack
  'マーカーにセルをピクチャ形式(emf形式)でコピーしたものを貼り付ける
  With myChart.SeriesCollection(1)
    For i = 1 To dataRange.Rows.Count
      dataRange.Columns(3).Cells(i).Resize(, 2).CopyPicture Appearance:=xlScreen, Format:=xlPicture
      .Points(i).Paste
    Next i
  End With
  'X軸設定
  With myChart.Axes(xlCategory)
    .MinimumScale = 0
    .MaximumScale = Application.WorksheetFunction.Max(labelRange.Columns(2))
    .MajorUnit = 1
    .HasMinorGridlines = False
'    .MinorUnit = 1
'    .TickLabelPosition = xlHigh
    '軸の目盛り数字を表示させない
    .TickLabels.NumberFormatLocal = """"""
  End With
  'Y軸設定
  With myChart.Axes(xlValue)
    .HasMajorGridlines = False
  End With
  'X軸に貼り付いているダミー系列にデータラベルを設定する
  For i = 1 To labelRange.Rows.Count
    With myChart.SeriesCollection(2).Points(i)
      .HasDataLabel = True
      .DataLabel.Left = .DataLabel.Left + dataLabelOffsetX
      .DataLabel.Top = .DataLabel.Top + datalabelOffsetY
      .DataLabel.Text = labelRange.Columns(1).Cells(i).Value
    End With
  Next i
End Sub

コンテンツリストに戻る


'===========================================================================
'☆散布図のグラフ上に矢印で二点間の距離を図示する(その1)

'データ構造 2点間の距離を図示する(→付き)
'   A B C D E F G H I J
'2 x y
'3 山田 -100 - 100
'4 山田 -60 - 60
'5 鈴木  80  100
'6 鈴木  70  70
'7 佐藤- 35  71
'8 佐藤- 10  5

' 系列を一個だけ用いる方法
Sub test()
  Dim mychartObj As ChartObject
  Dim targetRange As Range
  Dim mySeries As Series
  Dim i As Long
  Dim myPoint As Point
  Dim buf As Variant
  
  Set mychartObj = Sheets(1).ChartObjects.Add(50, 50, 250, 250)
  With Sheets(1)
    Set targetRange = Range(.Range("A3"), .Range("A" & .Rows.Count).End(xlUp)).Resize(, 3)
  End With
  
  With mychartObj.Chart
    Set mySeries = .SeriesCollection.NewSeries
    mySeries.XValues = targetRange.Columns(2)
    mySeries.Values = targetRange.Columns(3)
    .ChartType = xlXYScatter
    .HasLegend = False
    .Axes(xlCategory).HasMajorGridlines = True
  End With
  
  With mySeries
    .MarkerStyle = xlNone
    buf = .XValues
    '矢印の向きは、点の順番ではなく、左から右へであるか否かを
    '基準にしている様なので、前後の値を比較して矢印の向きを設定する
    For i = 2 To .Points.Count Step 2
      If buf(i) >= buf(i - 1) Then
        .Points(i).Format.Line.EndArrowheadStyle = msoArrowheadOpen
        .Points(i).Format.Line.EndArrowheadLength = msoArrowheadLong
        .Points(i).Format.Line.EndArrowheadWidth = msoArrowheadWide
      Else
        .Points(i).Format.Line.BeginArrowheadStyle = msoArrowheadOpen
        .Points(i).Format.Line.BeginArrowheadLength = msoArrowheadLong
        .Points(i).Format.Line.BeginArrowheadWidth = msoArrowheadWide
      End If
     .Points(i - 1).ApplyDataLabels
      .Points(i - 1).DataLabel.Text = targetRange.Cells(i, 1).Value
      .Points(i - 1).DataLabel.Format.TextFrame2.TextRange.Characters.Font.Fill.ForeColor.RGB = vbBlue
      .Points(i).Format.Line.Visible = msoTrue
      .Points(i).Format.Line.ForeColor.RGB = vbBlack
      .Points(i).Format.Line.Weight = 1.75
    
    Set mySeries = Nothing
  Next i
  End With
End Sub
'===========================================================================
'☆散布図のグラフ上に矢印で二点間の距離を図示する(その2)系列を多数生成する方法
Sub test0()
  Dim mychartObj As ChartObject
  Dim targetRange As Range
  Dim mySeries As Series
  Dim i As Long
  Dim myPoint As Point
  Dim buf As Variant
  
  Set mychartObj = ActiveSheet.ChartObjects.Add(50, 50, 250, 250)
  With Sheets(1)
    Set targetRange = Range(.Range("A3"), .Range("A" & .Rows.Count).End(xlUp)).Resize(, 3)
  End With
  With mychartObj.Chart
    .ChartType = xlXYScatter
    .HasLegend = False
    .Axes(xlCategory).HasMajorGridlines = True
  End With
  For i = 1 To targetRange.Rows.Count Step 2
    With mychartObj.Chart
      Set mySeries = .SeriesCollection.NewSeries
      mySeries.XValues = Range(targetRange.Columns(2).Cells(i), targetRange.Columns(2).Cells(i + 1))
      mySeries.Values = Range(targetRange.Columns(3).Cells(i), targetRange.Columns(3).Cells(i + 1))
    End With
  
    With mySeries
      buf = .XValues
      .Points(2).Format.Line.Visible = msoTrue
      If buf(2) >= buf(1) Then
        .Points(2).Format.Line.EndArrowheadStyle = msoArrowheadOpen
        .Points(2).Format.Line.EndArrowheadLength = msoArrowheadLong
        .Points(2).Format.Line.EndArrowheadWidth = msoArrowheadWide
      Else
        .Points(2).Format.Line.BeginArrowheadStyle = msoArrowheadOpen
        .Points(2).Format.Line.BeginArrowheadLength = msoArrowheadLong
        .Points(2).Format.Line.BeginArrowheadWidth = msoArrowheadWide
      End If
      .Points(2).Format.Line.ForeColor.RGB = vbBlack
      .Points(2).Format.Line.Weight = 1.75
      .Points(1).ApplyDataLabels
      .Points(1).DataLabel.Text = targetRange.Cells(i, 1).Value
      .Points(1).DataLabel.Format.TextFrame2.TextRange.Characters.Font.Fill.ForeColor.RGB = vbBlue
      .MarkerStyle = xlNone

  End With
  Next i
End Sub

コンテンツリストに戻る


'===========================================================================
'☆X軸の上側に0、±σ、±2σ、±3σを表示、Y軸に数字でなく科目名を表示、各科目の得点が何σの位置にあるのか、科目毎に異なるマーカー(色)で表示する。
 'シート構造(Worksheets(2))
'  B    C    D      E
'3 科目 No  正規化標準偏差  作業列
'4 社会 1    - 2   -3
'5 理科 2   0      -3
'6 数学 3   - 0.5  -3
'7 国語 4   1      -3
'8 全体 5   0.2    -3

Sub test()
  Dim sh As Worksheet
  Dim myChart As Chart
  Dim xValueRng1 As Range, xValueRng2 As Range, yValueRng As Range
  Dim i As Long
  Dim chartObjCount As Long
  
  Set sh = ThisWorkbook.Worksheets(2)
  'グラフの作成(データ系列は後で設定)
  chartObjCount = sh.ChartObjects.Count
  Set myChart = Charts.Add
  myChart.Location Where:=xlLocationAsObject, Name:=sh.Name
  With sh.ChartObjects(chartObjCount + 1)
     Set myChart = .Chart
    .Width = 400
    .Height = 250
  End With
  myChart.ChartType = xlXYScatter
  'グラフ化するデータ範囲を設定
  With sh
    Set xValueRng1 = Range(.Range("G4"), .Range("G" & .Rows.Count).End(xlUp))
    Set xValueRng2 = Range(.Range("H4"), .Range("H" & .Rows.Count).End(xlUp))
    Set yValueRng = Range(.Range("C4"), .Range("C" & .Rows.Count).End(xlUp))
  End With
  '系列の追加とマーカーの設定
  With myChart
    .SeriesCollection.NewSeries
    .SeriesCollection(1).XValues = xValueRng1
    .SeriesCollection(1).Values = yValueRng
    .SeriesCollection(1).Name = xValueRng1.Cells(0)
    .SeriesCollection(1).MarkerStyle = 8 '○
    .SeriesCollection(1).MarkerSize = 20
    
    .SeriesCollection.NewSeries
    .SeriesCollection(2).XValues = xValueRng2
    .SeriesCollection(2).Values = yValueRng
    .SeriesCollection(2).Name = xValueRng2.Cells(0)
    .SeriesCollection(2).MarkerStyle = -4142
    .HasLegend = False
    'データラベル移動の余地を準備
    .PlotArea.Width = .PlotArea.Width - 40
    .PlotArea.Left = .PlotArea.Left + 40
  End With
  'X軸の設定
'表示形式で、σを付与している
  With myChart.Axes(xlCategory)
    .MinimumScale = -3
    .MaximumScale = 3
    .MajorUnit = 1
    .HasMinorGridlines = True
    .MinorUnit = 1
    .TickLabelPosition = xlHigh
    .TickLabels.NumberFormatLocal = "0""σ"""
  End With
  'Y軸の設定
  With myChart.Axes(xlValue)
    .MinimumScale = 1
    .MaximumScale = xValueRng2.Cells.Count
    .MajorUnit = 1
    .HasMinorGridlines = True
    .MinorUnit = 1
    .TickLabelPosition = xlLow
    .TickLabels.NumberFormatLocal = """""" 'Y軸の数値を表示しない
  End With
  'データラベルの設定、位置調整、マーカー個別の色設定
  'データラベルの位置は微調整の事
  For i = 1 To yValueRng.Rows.Count
    With myChart.SeriesCollection(2).Points(i)
      'xl2007以降
      If CLng(Application.Version) > 11 Then
        myChart.SeriesCollection(1).Points(i).Format.Fill.ForeColor.RGB = yValueRng.Cells(i).Offset(0, -1).Interior.Color
      'xl2007より前
      Else
         myChart.SeriesCollection(1).Points(i).MarkerBackgroundColorIndex = yValueRng.Cells(i).Offset(0, -1).Interior.ColorIndex
      End If
      .HasDataLabel = True
      .DataLabel.Left = .DataLabel.Left - 40
      .DataLabel.Text = yValueRng.Cells(i).Offset(0, -1).Value
    End With
  Next i
End Sub

コンテンツリストに戻る


'===========================================================================
'☆散布図で異なる列のデータを統合して一つの系列としてプロットする方法をコード化
'サンプルデータ A3から始まる

	A	B	C	D	E	F
3	1	5	1.5	2.25	1.2	1.095
4	2	7	2.5	6.25	2.2	1.483
5	3	9	3.5	12.25	3.2	1.788
6	4	11	4.5	20.25	4.2	2.049
7	5	13	5.5	30.25	5.2	2.280
8	6	15	6.5	42.25	6.2	2.489
9	7	17	7.5	56.25	7.2	2.683
10	8	19	8.5	72.25	8.2	2.863


Sub makeGraph()
  Dim chartObj As ChartObject
  Dim targetRange As Range
  Dim strFormula As String
  Dim mySeries As Series
  
  Set chartObj = ActiveSheet.ChartObjects.Add(200, 1, 200, 300)
  With ActiveSheet
    Set targetRange = .Range(.Range("A3"), .Range("A" & .Rows.Count).End(xlUp)).Resize(, 6)
  End With
  With chartObj.Chart
    Set mySeries = .SeriesCollection.NewSeries
    mySeries.XValues = targetRange.Columns(1)
    mySeries.Values = targetRange.Columns(2)
    .HasTitle = True
    .HasLegend = False
    .ChartType = xlXYScatterLinesNoMarkers
  End With
  Call mergeSeries(mySeries, targetRange.Columns(3), targetRange.Columns(4))
  Call mergeSeries(mySeries, targetRange.Columns(5), targetRange.Columns(6))
End Sub

Sub mergeSeries(mySeries As Series, XValueRange As Range, ValueRange As Range)
  Dim i As Long
  Dim seriesParts As Variant 'Series.Formulaを分割した配列
  Dim newSeriesParts(0 To 3) As Variant '新しいFormula合成用
  Dim formulaParts As Variant 'XValue,Valueの式を分割した配列
  Dim buf As String
  Dim regEx As Object, Matches As Object
  Dim strXValues As String, strValues As String
  Dim addressArray() As Variant
  
  Set regEx = CreateObject("VBScript.RegExp")
  With regEx
    .MultiLine = False
    .Pattern = "\((.+?)\)"
    .IgnoreCase = True
    .Global = True   '全個数検索
  End With
  
  buf = Mid(mySeries.Formula, 9, Len(mySeries.Formula) - 9)
  '最初のカンマまでと、最後のカンマ以降を使用する。中間は、カンマの数が定まらないので、正規表現で処理する。
  seriesParts = Split(mySeries.Formula, ",")
  newSeriesParts(0) = seriesParts(0)
  newSeriesParts(3) = seriesParts(UBound(seriesParts))
  
  Set Matches = regEx.Execute(buf)
  If Matches.Count > 0 Then
    strXValues = Matches(0).submatches.Item(0)
    strValues = Matches(1).submatches.Item(0)
  Else
    '()が無い=初回。
    seriesParts = Split(mySeries.Formula, ",")
    strXValues = seriesParts(1)
    strValues = seriesParts(2)
  End If
  'XValues
  buf = strXValues
  'カンマが無くても要素数1,0ベースの配列が生成する
  formulaParts = Split(buf, ",")
  ReDim addressArray(0 To (UBound(formulaParts) + 1))
  For i = 0 To UBound(formulaParts)
    addressArray(i) = formulaParts(i)
  Next i
  addressArray(UBound(addressArray)) = XValueRange.Parent.Name & "!" & XValueRange.Address
  newSeriesParts(1) = "(" & Join(addressArray, ",") & ")"
  'Values
  buf = strValues
  formulaParts = Split(buf, ",")
  ReDim addressArray(0 To (UBound(formulaParts) + 1))
  For i = 0 To UBound(formulaParts)
    addressArray(i) = formulaParts(i)
  Next i
  addressArray(UBound(addressArray)) = XValueRange.Parent.Name & "!" & ValueRange.Address
  newSeriesParts(2) = "(" & Join(addressArray, ",") & ")"
  mySeries.Formula = Join(newSeriesParts, ",")
End Sub

コンテンツリストに戻る


'===========================================================================
'☆ADO Recordsetから直接グラフ作成(その1)
'.GetRowsを用いているので、レコード数の制約あり
Sub test()
  Dim cn As ADODB.Connection
  Dim rs As ADODB.Recordset
  Dim SQL As String
  Dim chart1 As Chart

  Set cn = New ADODB.Connection
  cn.Provider = "Microsoft.Jet.OLEDB.4.0"
  cn.Open ThisWorkbook.Path & "\db1.mdb"
  SQL = "SELECT * FROM Table1;"
  Set rs = New ADODB.Recordset
  rs.Open SQL, cn, adOpenStatic, adLockReadOnly
  If rs.BOF Then Exit Sub
  Set chart1 = Charts.Add(Before:=ActiveSheet)
  chart1.SeriesCollection.NewSeries
  '試験用データTable1のFields(0)はIDなので、Fields(1)から指定しています
  With chart1.SeriesCollection(1)
    .XValues = rs.GetRows(, , rs.Fields(1))
    rs.MoveFirst
    .Values = rs.GetRows(, , rs.Fields(2))
  End With
  Set rs = Nothing
  Set cn = Nothing
End Sub

'===========================================================================
'☆ADO Recordsetから直接グラフ作成(その2)
'xl2003以前ではデータ数に制約あり(Max20個)
Sub test()
  Dim cn As ADODB.Connection
  Dim rs As ADODB.Recordset
  Dim SQL As String
  Dim chart1 As Chart
  Dim i As Long
  Dim arrayX() As Date
  Dim arrayY() As Double

  Set cn = New ADODB.Connection
  cn.Provider = "Microsoft.Jet.OLEDB.4.0"
  cn.Open ThisWorkbook.Path & "\db1.mdb"
  SQL = "SELECT TOP 20 * FROM Table1 ORDER BY ID;"
  Set rs = New ADODB.Recordset
  rs.CursorLocation = adUseClient
  rs.Open SQL, cn, adOpenStatic, adLockReadOnly
  ReDim arrayX(rs.RecordCount - 1)
  ReDim arrayY(rs.RecordCount - 1)
  For i = 0 To rs.RecordCount - 1
    arrayX(i) = rs.Fields(1)
    arrayY(i) = rs.Fields(2)
    rs.MoveNext
  Next i
  Set chart1 = Charts.Add(Before:=ActiveSheet)
  chart1.ChartType = xlXYScatter
  chart1.SeriesCollection.NewSeries
  With chart1.SeriesCollection(1)
    .XValues = arrayX
    .Values = arrayY
  End With
  Set rs = Nothing
  Set cn = Nothing
End Sub

'===========================================================================
'☆ADO Recordsetから直接グラフ作成(その3)
'xl2003以前対応コード
Sub test()
  Dim cn As ADODB.Connection
  Dim rs As ADODB.Recordset
  Dim SQL As String
  Dim chart1 As Chart
  Dim i As Long
  Dim arrayX As Variant
  Dim arrayY As Variant

  Set cn = New ADODB.Connection
  cn.Provider = "Microsoft.Jet.OLEDB.4.0"
  cn.Open ThisWorkbook.Path & "\db1.mdb"
  SQL = "SELECT TOP 1000 * FROM Table1 ORDER BY ID;"
  Set rs = New ADODB.Recordset
  rs.CursorLocation = adUseClient
  rs.Open SQL, cn, adOpenStatic, adLockReadOnly
  With Worksheets(1)
    arrayX = .Range(.Cells(1), .Cells(rs.RecordCount, 1))
    arrayY = .Range(.Cells(1), .Cells(rs.RecordCount, 1))
  End With
  For i = 1 To rs.RecordCount
    arrayX(i, 1) = CDbl(rs.Fields(1))
    arrayY(i, 1) = rs.Fields(2)
  rs.MoveNext
  Next i
  ThisWorkbook.Names.Add Name:="Date", RefersTo:=arrayX
  ThisWorkbook.Names.Add Name:="Rate", RefersTo:=arrayY
  Set chart1 = Charts.Add(Before:=ActiveSheet)
  chart1.ChartType = xlXYScatter
  chart1.SeriesCollection.NewSeries
  With chart1.SeriesCollection(1)
    .XValues = "='" & ThisWorkbook.Name & "'!Date"
    .Values = "='" & ThisWorkbook.Name & "'!Rate"
  End With
  chart1.Axes(xlCategory).TickLabels.NumberFormatLocal = "yyyy/m/d"
  Set rs = Nothing
  Set cn = Nothing
End Sub

コンテンツリストに戻る


'===========================================================================
'☆グラフに基準線を追加する(Y軸の値は対話的に設定)
Sub test()
  Dim myChart As Chart
  Dim myObj As Object
  Dim mySeries As Series
  Dim xMax As Double, xMin As Double
  Dim yStd As Variant
  
  If InStr(TypeName(Selection), "Chart") = 0 Then Exit Sub
  Set myObj = Selection
  yStd = Application.InputBox(Prompt:="Y軸の基準値を入力してください。", Type:=1)
    
  Do Until TypeName(myObj) = "Chart"
    Set myObj = myObj.Parent
  Loop
  Set myChart = myObj
  With myChart
    xMin = .Axes(xlCategory).MinimumScale
    xMax = .Axes(xlCategory).MaximumScale
    Set mySeries = .SeriesCollection.NewSeries
  End With
  With mySeries
    .XValues = Array(xMin, xMax)
    .Values = Array(yStd, yStd)
  End With
End Sub

コンテンツリストに戻る


'第二軸の上の方に別の数値目盛りを表示して、別の折れ線グラフを表示したいというお題
'エクセルでやる事じゃないが、無理矢理やってみる - 不可視の折れ線グラフを目盛りの数だけ引いて、最終点のデータラベルを目盛り数字に使用
'お勧めできないので、投稿はしなかった
'Yoffsetのところと、ratioのところはセルに範囲名をつけて、コード及びC系列の値算出に使用している

	2007	2008	2009	2010	2011			
A	1	2	3	4	5			
B	2	4	6	8	10		Yoffset	ratio
C	87	84	81	78	75		70	10
C0	170	140	110	80	50

Sub test()
  Dim mySh As Worksheet
  Dim myChartObj As ChartObject
  Dim mySeries As Series
  Dim myMax As Double, myMin As Double, myMajorUnit As Double
  Dim dataCount As Long, axisLabelCount As Long, i As Long, j As Long
  Dim myXValues() As Variant, myValues() As Variant
  Const dataLabelOffsetX As Double = 30
  
  Set mySh = ThisWorkbook.Sheets(1)
  Set myChartObj = mySh.ChartObjects(1) '目的のシートにグラフが一個だけあるとする
  With myChartObj.Chart
    myMin = .Axes(xlValue, xlSecondary).MinimumScale
    myMax = .Axes(xlValue, xlSecondary).MaximumScale
    myMajorUnit = .Axes(xlValue, xlSecondary).MajorUnit
    '軸の目盛り数字を表示させない
    .Axes(xlValue, xlSecondary).TickLabels.NumberFormatLocal = """"""
  End With
  '4番以降の系列クリア
  On Error Resume Next
  With myChartObj.Chart
    For i = 4 To 100 'この100は適当な大きな数
      .SeriesCollection(4).Delete
    Next i
  End With
  On Error GoTo 0
  
  dataCount = UBound(myChartObj.Chart.SeriesCollection(1).XValues)
  axisLabelCount = (myMax - myMin) / myMajorUnit + 1
  For i = 1 To axisLabelCount
    ReDim myXValues(1 To dataCount)
    ReDim myValues(1 To dataCount)
    Set mySeries = myChartObj.Chart.SeriesCollection.NewSeries
    For j = 1 To dataCount
      myValues(j) = myMin + myMajorUnit * (i - 1)
    Next j
    With mySeries
      .Values = myValues
      .XValues = myChartObj.Chart.SeriesCollection(1).XValues
      .ChartType = xlLine
      .AxisGroup = 2
      .Points(dataCount).HasDataLabel = True
      .Points(dataCount).DataLabel.Left = .Points(dataCount).DataLabel.Left + dataLabelOffsetX
      If myMin + myMajorUnit * (i - 1) >= mySh.Range("Yoffset") Then
        .Points(dataCount).DataLabel.Text = (myMin + myMajorUnit * (i - 1) - mySh.Range("Yoffset")) * mySh.Range("ratio")
      Else
        .Points(dataCount).DataLabel.Text = myMin + myMajorUnit * (i - 1)
      End If
      .Format.Line.Visible = msoFalse
    End With
  Next i
  '凡例を削除
  With myChartObj.Chart
    For i = 4 To .Legend.LegendEntries.Count
      .Legend.LegendEntries(4).Delete
    Next i
  End With
End Sub
コンテンツリストに戻る

	
'グラフをタイトルでソートして並び替える
'連想配列(Dictionary)にグラフタイトルをキーに、ChartObjectを入れておいて、キー配列を並び替えた後、新しい順番で読み出す
'最初別のシートへの移動を考えたが、ChartObjectへの参照が途切れてしまう様なので同じシート内とした。
Sub sortGraphByTitle()
  Dim myChartObj As ChartObject
  Dim sh As Worksheet
  Dim myDic As Object
  Dim myKeys As Variant
  Dim i As Long
  Dim counter As Long, graphColumns As Long
  Dim myLeft As Double, myTop As Double, myWidth As Double, myHeight As Double
  Dim xOffset As Double, yOffset As Double
  
  graphColumns = 3
  myWidth = 200: myHeight = 150
  xOffset = 20: yOffset = 20
  
  Set sh = ThisWorkbook.Sheets(1)
  Set myDic = CreateObject("Scripting.Dictionary")
  For Each myChartObj In sh.ChartObjects
    myDic.Add myChartObj.Chart.ChartTitle.Text, myChartObj
  Next myChartObj
  myKeys = myDic.keys
  sortkeys myKeys
  For i = LBound(myKeys) To UBound(myKeys)
    Set myChartObj = myDic(myKeys(i))
    myLeft = 150 + (counter Mod graphColumns) * (myWidth + xOffset)
    myTop = 10 + (counter \ graphColumns) * (myHeight + yOffset)
    With myChartObj
      .Left = myLeft
      .Top = myTop
    End With
    counter = counter + 1
    Set myChartObj = Nothing
  Next i
End Sub

'キーをソーティングする
Sub sortkeys(myKeys As Variant)
  Dim workSh As Worksheet
  Dim sortRange As Range
  
  Set workSh = ThisWorkbook.Sheets(2)
  workSh.Cells.Clear
  workSh.Range("A1:A" & (UBound(myKeys) + 1)).Value = Application.WorksheetFunction.Transpose(myKeys)
  Set sortRange = workSh.Range("A1").CurrentRegion
  'ここでは単純にA列で並び替えているが、A列から特定の字句をB列に抽出し、
  'B列の値をキーに、ユーザー定義リストにより並び替え、A列を書き戻すといった事もできる
  'その場合は、key1をRange("B1")等に変更する
  sortRange.Sort key1:=sortRange.Range("A1"), Order1:=xlAscending
  
  myKeys = Application.WorksheetFunction.Transpose(sortRange.Columns(1).Value)
End Sub

'下記はおまけ
'テスト用のグラフを作る
Sub makeGraphMatrix()
  Dim targetRange As Range, myArea As Range, graphDataArea As Range
  Dim myChartObject As ChartObject
  Dim sh As Worksheet
  Dim counter As Long, graphColumns As Long
  Dim myLeft As Double, myTop As Double, myWidth As Double, myHeight As Double
  Dim xOffset As Double, yOffset As Double
  Dim chartObj As ChartObject
  
  graphColumns = 3
  myWidth = 200: myHeight = 150
  xOffset = 20: yOffset = 20
  Set sh = ThisWorkbook.Sheets(1)
  With sh
    Set targetRange = .Range(.Range("A1"), .Range("A" & .Rows.Count).End(xlUp))
  End With
  For Each myArea In targetRange.SpecialCells(xlCellTypeConstants).Areas
    Set graphDataArea = Intersect(myArea, myArea.Offset(1, 0)).Resize(, 3)
    myLeft = 150 + (counter Mod graphColumns) * (myWidth + xOffset)
    myTop = 10 + (counter \ graphColumns) * (myHeight + yOffset)
    Set chartObj = sh.ChartObjects.Add(myLeft, myTop, myWidth, myHeight)
    With chartObj.Chart
      .HasTitle = True
      .ChartTitle.Text = myArea.Cells(1).Value
    End With
    makeGraph graphDataArea, chartObj
    counter = counter + 1
  Next myArea
End Sub

'グラフ作成のサブルーチン
Sub makeGraph(myRange As Range, myChartObj As ChartObject)
  Dim mySeries As Series
  Dim i As Long
  
  With myChartObj.Chart
    Set mySeries = .SeriesCollection.NewSeries
    mySeries.XValues = myRange.Columns(1)
    mySeries.Values = myRange.Columns(2)
    .ChartType = xlColumnClustered
    .HasLegend = False
  End With
  For i = 1 To mySeries.Points.Count
    With mySeries.Points(i)
      .HasDataLabel = True
      .DataLabel.Text = myRange.Columns(3).Cells(i).Value
    End With
  Next i
End Sub

'Testデータを作る
Sub makeTestData()
  Dim i As Long, j As Long, dataCount As Long
  Dim mycell As Range
  Dim myCollection As Collection
  Dim pickUp As Long
  
  'Collectionにアルファベット大文字26文字を収納
  Set myCollection = New Collection
  For i = 1 To 26
    myCollection.Add Item:=Chr(64 + i)
  Next i
  
  Randomize Time
  ThisWorkbook.Sheets(1).Cells.Clear
  Set mycell = ThisWorkbook.Sheets(1).Range("A1")
  For i = 1 To 26
    '
    pickUp = Int(Rnd() * myCollection.Count) + 1
    mycell.Value = myCollection(pickUp)
    myCollection.Remove pickUp
    Set mycell = mycell.Offset(1, 0)
    dataCount = Int(10 * Rnd()) + 1
    For j = 1 To dataCount
      mycell.Value = j
      mycell.Offset(0, 1).Value = 20 * Rnd
      Set mycell = mycell.Offset(1, 0)
    Next j
    Set mycell = mycell.Offset(1, 0)
  Next i
End Sub

コンテンツリストに戻る


Sub makePieGraphOnMemory()
  Dim myWbk As Workbook
  Dim mySh As Worksheet
  Dim hitRange As Range, targetRange As Range
  Dim buf As Variant, mainArray() As Variant, subArray() As Variant
  Dim i As Long
  Dim searchWord As String, myKey As String
  Dim myDic As Object
  Dim myChart As Chart, mySeries As Series
  
  searchWord = "ドロップアイテム"
  Set myWbk = ThisWorkbook
  Set mySh = myWbk.Worksheets(1)
  Set hitRange = mySh.UsedRange.Find(What:=searchWord _
                        , After:=mySh.UsedRange.Cells(1) _
                        , LookIn:=xlValues _
                        , LookAt:=xlWhole _
                        , SearchOrder:=xlByRows _
                        , SearchDirection:=xlNext _
                        , MatchCase:=False _
                        , MatchByte:=False _
                        , SearchFormat:=False)
  'SearchFormatをTrueにするとセルの書式を検索できます xl2002以降
  If hitRange Is Nothing Then Exit Sub
  With mySh
    Set targetRange = .Range(hitRange.Item(2), hitRange.End(xlDown))
  End With
  buf = targetRange.Value
  Set myDic = CreateObject("Scripting.Dictionary")
  '決められた順にグラフにしたければDictionaryにあらかじめキーと値0を入力してやれば、ユーザー定義リストによる並び替え的に使える
  For i = 1 To UBound(buf, 1)
    myKey = CStr(buf(i, 1))
    If Not myDic.exists(myKey) Then
      myDic.Add myKey, 1
    Else
      myDic(myKey) = myDic(myKey) + 1
    End If
  Next i
  Set myChart = Charts.Add(Before:=mySh)
  myChart.ChartType = xlPie
  Set mySeries = myChart.SeriesCollection.NewSeries
  '直接 .XValues = myDic.keys,.Values = myDic.itemsでも良いが、比率の大きい順にソートしてみた
  mainArray = myDic.items
  subArray = myDic.keys
  QuickSort mainArray, subArray, LBound(mainArray), UBound(mainArray)
  
  mySeries.XValues = subArray
  mySeries.Values = mainArray
  myChart.HasTitle = True
  myChart.HasLegend = False
  myChart.ChartTitle.Text = hitRange.Value
  mySeries.ApplyDataLabels xlDataLabelsShowLabelAndPercent
End Sub

'昔Webでいただいたコード。出所は既にリンク切れでした。
Sub QuickSort(vntSortData() As Variant, subSortData(), lngMin As Long, lngMax As Long)
  Dim lngIdxL        As Long
  Dim lngIdxR        As Long
  Dim vntKijunChi    As Variant
  Dim vntWk          As Variant
  
  vntKijunChi = vntSortData((lngMin + lngMax) \ 2)
  lngIdxL = lngMin
  lngIdxR = lngMax
  Do
    For lngIdxL = lngIdxL To lngMax Step 1
      If (vntSortData(lngIdxL) <= vntKijunChi) Then '降順は『>=』を『<=』 にする。
        Exit For
      End If
    Next
    For lngIdxR = lngIdxR To lngMin Step -1
      If (vntSortData(lngIdxR) >= vntKijunChi) Then '降順は『<=』を『>=』 にする。
        Exit For
      End If
    Next
    If lngIdxL >= lngIdxR Then
      Exit Do
    End If
    vntWk = vntSortData(lngIdxL)
    vntSortData(lngIdxL) = vntSortData(lngIdxR)
    vntSortData(lngIdxR) = vntWk
    vntWk = subSortData(lngIdxL)
    subSortData(lngIdxL) = subSortData(lngIdxR)
    subSortData(lngIdxR) = vntWk
    lngIdxL = lngIdxL + 1
    lngIdxR = lngIdxR - 1
  Loop
  If (lngMin < lngIdxL - 1) Then
    QuickSort vntSortData(), subSortData(), lngMin, lngIdxL - 1
  End If
  If (lngMax > lngIdxR + 1) Then
    QuickSort vntSortData(), subSortData(), lngIdxR + 1, lngMax
  End If
End Sub

コンテンツリストに戻る



'散布図のY軸にダミーの系列を貼り付けて、Y軸の目盛り数値の代わりにデータラベルを表示し
'手動で修正できる様にする。(異なるオーダーのデータを一つのグラフ上に表示する援用)
'散布図は比較的容易だが、棒グラフの場合はもっと面倒(上述7の記事参照)
'X,Y軸共に目盛り数値の最大値、最小値、目盛り間隔は手動で設定してある事が前提
Sub addDummySeries()
  Dim myWbk As Workbook
  Dim mySh As Worksheet
  Dim myChartObj As ChartObject
  Dim dummySeries As Series
  Dim myXValues As Variant, myValues As Variant
  Dim i As Long, seriesCount As Long
  
  Set myWbk = ThisWorkbook
  Set mySh = myWbk.Sheets("Sheet1")
  'シートにグラフは一個だけである事を前提
  Set myChartObj = mySh.ChartObjects(1)
  'X,Y軸共に目盛り数値の手動で設定を確認
  If axisScaleIsNotAuto(myChartObj.Chart) Then
    MsgBox "各軸目盛りの最大、最小、目盛り間隔は手動で設定して下さい。"
    Exit Sub
  End If
  'Y軸の目盛り数値を読み込んでダミー系列用のデータを生成
  With myChartObj.Chart.Axes(xlValue)
    ReDim myXValues(1 To (.MaximumScale - .MinimumScale) / .MajorUnit + 1)
    ReDim myValues(1 To (.MaximumScale - .MinimumScale) / .MajorUnit + 1)
    For i = 1 To UBound(myValues)
      myValues(i) = .MinimumScale + .MajorUnit * (i - 1)
      myXValues(i) = myChartObj.Chart.Axes(xlCategory).MinimumScale
    Next i
    'Y軸の目盛り数値を非表示
    .TickLabels.NumberFormatLocal = """"""""""
  End With
  '新系列追加
  Set dummySeries = myChartObj.Chart.SeriesCollection.NewSeries
  seriesCount = myChartObj.Chart.SeriesCollection.Count
  Debug.Print seriesCount
  With dummySeries
    .XValues = myXValues
    .Values = myValues
    .HasDataLabels = True
    .DataLabels.Position = xlLabelPositionLeft
    .Format.Line.Visible = msoFalse
  End With
  '追加したダミー系列の凡例削除
  On Error Resume Next '凡例が表示されず、エラーで止まる事がある対策
  myChartObj.Chart.Legend.LegendEntries(seriesCount).Delete
End Sub

'各軸目盛りの最大、最小、目盛り間隔は手動で設定してあるか判定
Function axisScaleIsNotAuto(myChart As Chart) As Boolean
  With myChart.Axes(xlValue)
    If .MaximumScaleIsAuto Or .MinimumScaleIsAuto Or .MajorUnitIsAuto Then
      axisScaleIsNotAuto = True
      Exit Function
    End If
  End With
  With myChart.Axes(xlCategory)
    If .MaximumScaleIsAuto Or .MinimumScaleIsAuto Or .MajorUnitIsAuto Then
      axisScaleIsNotAuto = True
      Exit Function
    End If
  End With
End Function

'***** 下記は、選択しにくいダミー系列を削除したいときに使用 *****

'凡例を持たないSeriesを削除する
'http://msdn.microsoft.com/en-us/library/office/aa272310(v=office.11).aspx
Sub deleteSeriesHasNoLegend()
  Dim myWbk As Workbook
  Dim mySh As Worksheet
  Dim myChartObj As ChartObject
  Dim mySeries As Series
  
  Set myWbk = ThisWorkbook
  Set mySh = myWbk.Sheets("Sheet1")
  'シートにグラフは一個だけである事を前提
  Set myChartObj = mySh.ChartObjects(1)
  
  For Each mySeries In myChartObj.Chart.SeriesCollection
    If Not seriesHasLegend(mySeries) Then mySeries.Delete
  Next mySeries
End Sub

'Seriesが凡例を持つか判定
Function seriesHasLegend(mySeries As Series) As Boolean
  Dim myChart As Chart
  Dim i As Long, seriesIndex As Long
  Dim myLegendEntry As LegendEntry
  
  Set myChart = mySeries.Parent.Parent
  For i = 1 To myChart.SeriesCollection.Count
    If myChart.SeriesCollection(i).Name = mySeries.Name Then
      seriesIndex = i
      Exit For
    End If
  Next i
  On Error Resume Next
  Set myLegendEntry = myChart.Legend.LegendEntries(seriesIndex)
  If Err.Number = 0 Then
    seriesHasLegend = True
  Else
    seriesHasLegend = False
  End If
End Function
コンテンツリストに戻る


'コメント表示用補助系列のマーカーとして、あらかじめ準備しておいたひな形用図形のテキストのみ変更して貼り付けます
'ひな型を準備しておくことで、コードはとても簡単になりました。
'貼り付けたマーカー図形は、マーカーを自動にすると削除されます。再度、手動に戻せばOK。
Sub pasteShapes()
  Dim myChart As Chart
  Dim myShape As Shape
  Dim dataRange As Range, myCell As Range
  Dim i As Long
  
  'A2を先頭に、A列がxValue、B列がValue、C列がコメント用補助系列、D列の必要なセルにコメント文字列
  Set dataRange = ActiveSheet.Range("A2").CurrentRegion
  'Activesheetにグラフが一つだけあるとします。
  Set myChart = ActiveSheet.ChartObjects(1).Chart
  '第二系列はコメント表示用に適宜オフセットを取った系列を設けます
  With myChart.SeriesCollection(2)
    For i = 2 To dataRange.Rows.Count
      Set myCell = dataRange.Cells(i, 4) '4列目(D列)がコメント列
      If myCell.Value <> "" Then
        'ひな形図形の名前は、図形をクリックすると画面上部のセル座標が表示されるところに表示されるものです。
        Set myShape = ActiveSheet.Shapes("角丸四角形吹き出し 4").Duplicate
        myShape.TextFrame2.TextRange.Characters.Text = myCell.Value
        myShape.Cut
        .Points(i - 1).Paste
      End If
    Next i
  End With
End Sub