2016年5月11日水曜日

グラフ化するデータの構造

1. 測定データの例


 以下のようなデータを考えます。温度を変えながら測定した結果を想定します。適当な関数を使って、データを生成しました。測定サンプル(N=5)としていて、少し特性を変えています。また、測定方法を変えた場合を想定して、「MeasureA」, 「MeasureB」と2つの結果を配置しました。

  このデータの配置方法では、横軸を温度で表示するグラフは簡単に作成できますが、それ以外の値を横軸に取った場合は少し 手間が増えます。左下のグラフに作りましたが、例えば5つの測定サンプルはサイズを変えて試作した試料だとします。その場合は、サイズに対する依存性を確認する必要があり、横軸をサイズ、縦軸を測定値(MeasureA@80℃)を取り出しました。

 Excelで作業することで、データからグラフ作成の操作などが視覚的でわかりやすいというのは利点なのですが、繰り返し処理をすることを考えると効率が下がります。
 
 そこで、Accessデータベースにデータを格納して、必要な形でデータを抽出&グラフ作成をするプログラムを作成します。





2. Accessデータベースに入力

Access2013にデータを入力します。テーブルを設計して、そのテーブルにデータを入力します。テーブルの設計といっても、簡単なテーブルでの例となります。テーブルは、図のように定義します。
  • Name:測定名
  • Sample:サンプル名
  • Temp:温度
  • Data:測定結果 
 を作成しました。



次に、データを入力します。入力した結果が下図のようになります。そのままグラフに作りにくい形式のように見えますがデータを取り出すときのクエリを工夫することで効率的にグラフを作成します。データの保存をAcess, グラフの作成をExcelでやります。




3. データ抽出のサンプルプログラムの実行結果

 データを抽出した結果を示します。データベースのファイル名と測定名、サンプル名を入力として、その条件での測定結果を抽出するというプログラムになっています。TempとDataのペアになっているのでこのデータを使ってグラフを作成することができる形です。この例は読み出したデータをそのままセルに入力ものとなっています。

 


 4. プログラム紹介

プログラムのソースコードを示します。


Private Sub ReadDataBase_Click()


    Dim ado_con_obj     As New ADODB.Connection
    Dim ado_rs_obj      As New ADODB.Recordset
    Dim sql_string      As String
    Dim db_path      As Variant
    
    db_path = ActiveWorkbook.Path & "\" & Range("H8").Value
 
    ado_con_obj.ConnectionString = "provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & db_path
    ado_con_obj.Open
 
    
    sql_string = " SELECT T_データ.[Name], T_データ.[Sample], T_データ.[Temp], T_データ.[Data]" _
               & " FROM T_データ WHERE (((T_データ.Name)='" & Range("H9").Value & "') AND ((T_データ.[Sample])='" & Range("H10").Value & "')) ;"

    ado_rs_obj.Open sql_string, ado_con_obj, adOpenDynamic

    
    Dim i, j As Integer
    
    i = 1

    '最初の列に、Nameを入れます。
    For j = 0 To ado_rs_obj.Fields.Count - 1
        Cells(i, j + 1) = ado_rs_obj.Fields(j).Name
    Next

    i = 2

    'レコードの最後まで順番に読み出してセルに入力します。
    Do Until ado_rs_obj.EOF
    
        For j = 0 To ado_rs_obj.Fields.Count - 1
            Cells(i, j + 1) = ado_rs_obj.Fields(j).Value
        Next
        
        ado_rs_obj.MoveNext
    
        i = i + 1
    
    Loop
    
    
    
End Sub


2016年5月9日月曜日

グラフ自動作成プログラムの動作確認

1. 目標となるグラフ

  プログラムの動作を確認するために図のようなグラフを目標に動作確認してみます。下の図は手作業で作成した散布図になります。データは、適当に関数を使って作ったものになります。
  図の左側のセルに打ち込んである情報がグラフを作成するために必要な情報です。これを使ってグラフを作ります。繰り返しになりますが、これは手作業で作ったグラフです。グラフのこのテイストは、完全に私の好みです。いつもこういったグラフを作っています。


 

2. プログラムで自動作成したグラフ

今回作ったクラスモジュールで作ったグラフです。フォントのサイズやマーカーの形が若干異なりますが、悪くない出来だと思います。ここではシートに打ち込んだデータからグラフを作成していますが、最終目標は、Accessから読み込んだデータをグラフにして、pptへ貼り付けてレポートの雛型を作ることで、最後にすこしコメントを足して、そのまま完成というのが目標です。



 


 プログラム は、以下のような感じです。最初に「chart_obj」オブジェクトを作成して、そこにプロパティを設定していきます。「series_obj」オブジェクトはデータを追加する部分になっていますが、ベタ書きっぽくなっているのは、最終目標がAccessデータベースからの入力なので、Rangeで一括指定するといった方法は想定していないことが理由です。むしろセルからうまく指定するというプログラムを作るつもりはありません。
 ポイントとしては、プロパティを設定していく操作を、手作業でチャートを作るときの操作に対応させて、処理の粒度もそれくらいにしていることです。プログラムの中身を忘れてしまっても、このコードを見れば、何をする処理なのかがすぐわかることと、中身を知らなくても再利用できるような表現にひとまず、できてるかぁなと思っています。 ということで、ひとまずグラフ作成の部分はこんな感じです。

Private Sub Chart_Create_Button_Click()

    Dim chart_obj As Chart_Class
    Dim series_obj As Series_Class
    
    Set chart_obj = New Chart_Class

    
    'チャートの位置とサイズを決定
    chart_obj.chartLeft = 300
    chart_obj.chartTop = 100
    chart_obj.chartWidth = 400
    chart_obj.chartHeight = 300
    
    '軸の情報を決められたセルから取ってきて、設定
    chart_obj.xTitle = Range("C3").Value
    chart_obj.xMinScale = Range("C4").Value
    chart_obj.xMaxScale = Range("C5").Value
    
    chart_obj.yTitle = Range("D3").Value
    chart_obj.yMinScale = Range("D4").Value
    chart_obj.yMaxScale = Range("D5").Value
    
    
    'データの設定 data1 セルを指定
    Set series_obj = New Series_Class
    series_obj.Name = Range("C7")
    Call series_obj.SetxDataByRange(Range("B8:B28"))
    Call series_obj.SetyDataByRange(Range("C8:C28"))
    chart_obj.seriseData = series_obj
    
    
    'データの設定 data2 セルを指定
    Set series_obj = New Series_Class
    series_obj.Name = Range("D7")
    Call series_obj.SetxDataByRange(Range("B8:B28"))
    Call series_obj.SetyDataByRange(Range("D8:D28"))
    chart_obj.seriseData = series_obj
    
    
    'データの設定 data3 セルを指定
    Set series_obj = New Series_Class
    series_obj.Name = Range("E7")
    Call series_obj.SetxDataByRange(Range("B8:B28"))
    Call series_obj.SetyDataByRange(Range("E8:E28"))
    chart_obj.seriseData = series_obj

End Sub


クラスモジュールの使い方

1. はじめに


 「マクロの記録」を使いながら、グラフ作成を自動化するプログラムを作りましたが、すべてをベタに書いてしまったので、再利用しにくい動作確認のためだけに使うものになってしまいました。
  これから繰り返し使うことを考えて、クラスモジュールで実装しようと思います。

2. プログラムの説明

  まずは、ソースコードを載せます。Class_Initializeはオブジェクトが生成されたときに実行されるSubなので、初期化をします。チャート作成に使うオブジェクトの生成と変数を初期化します。それ以外の Property Get と Property Letはペアになっていて、プロパティを作ります。

クラスモジュールのソースコード

Private x_title As String

Private y_title As String
Private x_minimum_scale As Double
Private x_maximum_scale As Double
Private y_minimum_scale As Double
Private y_maximum_scale As Double
Private chart_left As Integer
Private chart_top As Integer
Private chart_width As Integer
Private chart_height As Integer
Private chart_title As String

'グラフ用のオブジェクト
Private shape_obj As Shape
Private chart_obj As Chart
Private series_obj As Series
Private series_collection_obj As SeriesCollection
Private x_axis_obj As Axis
Private y_axis_obj As Axis

Const major_unit_div As Integer = 5
Const width_heigh_ratio = 1.25

Private series_count As Integer

'初期化
Public Sub Class_Initialize()
    
    x_title = ""
    y_title = ""
    
    chart_title = ""
    
    'チャートの作成と必要なオブジェクトを作る
    Set shape_obj = ActiveSheet.Shapes.AddChart(xlXYScatter, 0, 0, 375, 300)
    Set chart_obj = shape_obj.Chart
    Set series_collection_obj = chart_obj.SeriesCollection
    Set series_obj = series_collection_obj.NewSeries
    Set x_axis_obj = chart_obj.Axes(xlCategory)
    Set y_axis_obj = chart_obj.Axes(xlValue)
    
    chart_obj.SetElement (msoElementPrimaryCategoryGridLinesMajor)
    
    x_axis_obj.TickLabelPosition = xlTickLabelPositionLow
    y_axis_obj.TickLabelPosition = xlTickLabelPositionLow
    
    'ひとまず、デフォルトを0~1とする。
    x_minimum_scale = 0
    x_maximum_scale = 1
    y_minimum_scale = 0
    y_maximum_scale = 1
    
    
    x_axis_obj.MajorUnit = (x_maximum_scale - x_minimum_scale) / major_unit_div
    y_axis_obj.MajorUnit = (y_maximum_scale - y_minimum_scale) / major_unit_div
         
    series_count = 0
         
End Sub


Public Property Get chartTitle() As String
    
    chartTitle = chart_title

End Property

Public Property Let chartTitle(chart_title_in As String)
    chart_title = chart_title_in
    
      With chart_obj
        .HasTitle = True
        .chartTitle.Text = chart_title
    
    End With

End Property


Public Property Get chartLeft() As Integer
    
    chartLeft = chart_left

End Property

Public Property Let chartLeft(chart_left_in As Integer)
    
    chart_left = chart_left_in
    shape_obj.Left = chart_left
    
End Property

Public Property Get chartTop() As Integer
    
    chartTop = chart_top

End Property

Public Property Let chartTop(chart_top_in As Integer)
    
    chart_top = chart_top_in
    shape_obj.Top = chart_top
    
End Property

Public Property Get chartWidth() As Integer
    
    chartWidth = chart_width

End Property

Public Property Let chartWidth(chart_width_in As Integer)
    
    chart_width = chart_width_in
    shape_obj.Width = chart_width
    
End Property

Public Property Get chartHeight() As Integer
    
    chartHeight = chart_height

End Property

Public Property Let chartHeight(chart_height_in As Integer)
    
    chart_height = chart_height_in
    shape_obj.Height = chart_height
    
End Property






 クラスモジュールを呼び出す部分のソースコードです 。グラフ作成に使うプロパティだけを実装するようにしておけば、後々使う時でも、プロパティを見ながら入力を決めていけばよいので、再利用しやすそうです。標準で用意されているオブジェクトを使って、そのままプログラムを書くと選択できるプロパティが多すぎて、いちいち記憶できません。ソフト開発を本職にしているわけではないので、なるべく楽をしたいです。このソースは、全体の一部分になっていませす。動作説明用に切り出しました。

Private Sub Class_Module_Button_Click()

    Dim chart_obj As Chart_Class_Symple
    Set chart_obj = New Chart_Class_Symple
    
    
    With chart_obj
        .chartTitle = "Chart Title"
        .chartLeft = 50
        .chartTop = 50
        .chartHeight = 300
        .chartWidth = .chartHeight * 1.25
    End With
    
End Sub



3. クラスモジュールへ置き換え

基本ルールとしては、忘れてしまっても後で簡単に思い出せるようにプロパティを絞って実装する。また、プロパティに値を設定した時にチャートも更新する。 と、しました。
  実行するとこのようになります。チャートを作る位置とサイズ、チャートのタイトルをプロパティに設定するとそれを反映したグラフができます。次は、プログラム全体を紹介したいと思います。




2016年5月7日土曜日

Excelでチャートを作成するVBAの雛型

1. 出来上がりのイメージ

 Excel VBAでチャートの作成を自動化したいので、チャートを作成するプログラムの練習をします。最終的な自動化のプログラムではなく、いろんな機能を試した時のソースコードを説明します。
 まず、出来上がりの画面です。





2. ソースコードの説明

 あまり細かい説明はしないのですが、このようなコードでチャートを作成しました。だいたい、これくらいの機能が使えれば、狙ったチャートを作れると思います。最終的には、Accessデータベースに保存したデータを読み込んで、チャート化してpptに貼るという一連のプログラムを作りたいと思います。


Private Sub Chart_Create_Button_Click()

    Dim size As Integer
    Dim ShapeObj As Shape
    Dim SeriesObj As Series
    Dim ScObj As SeriesCollection
    Dim ChartObj As Chart
    Dim XaxisObj As Axis
    Dim YaxisObj As Axis
    
    Dim x_index As Integer
    Dim y_index As Integer
    
    x_index = 0
    y_index = 0
    size = 300
    
    
    Set ShapeObj = ActiveSheet.Shapes.AddChart(xlXYScatter, x_index * size * 1.2, y_index * size, size * 1.2, size)
    
    Set ChartObj = ShapeObj.Chart
    Set ScObj = ChartObj.SeriesCollection
    
    Set SeriesObj = ScObj.NewSeries
   
    'データを設定する
    SeriesObj.XValues = DataGen(20)
    SeriesObj.Values = DataGen(20)
       
    SeriesObj.Name = "test_name1"
    
    
    Set SeriesObj = ScObj.NewSeries
   
    'データを設定する
    SeriesObj.XValues = DataGen(20)
    SeriesObj.Values = DataGen(20)
       
    SeriesObj.Name = "test_name2"
    
    
    Set Xaxis = ChartObj.Axes(xlCategory)
    Set Yaxis = ChartObj.Axes(xlValue)
    
    
   '横軸ラベルを設定する
    With Xaxis
        .HasTitle = True
        .AxisTitle.Text = "test_Axis_x"
    End With
    
    '縦軸ラベルを設定する
    With Yaxis
        .HasTitle = True
        .AxisTitle.Text = "test_Axis_y"
    End With

    Xaxis.MaximumScale = 2
    Xaxis.MinimumScale = -1
    Yaxis.MaximumScale = 2
    Yaxis.MinimumScale = -1
    
    
    ChartObj.SetElement (msoElementPrimaryCategoryGridLinesMajor)
    
    Xaxis.TickLabelPosition = xlLow
    Yaxis.TickLabelPosition = xlLow
    
    With ChartObj
        .HasTitle = True
        .ChartTitle.Text = "ChartTitle"
    
    End With
    
End Sub

Public Function DataGen(data_width As Integer) As Double()
    Dim i As Integer
    Dim DataArray() As Double
    
    
    ReDim DataArray(data_width)
    
    For i = 0 To data_width - 1
        DataArray(i) = Rnd()
    Next

    DataGen = DataArray
End Function



2016年5月6日金曜日

チャート作成プログラムの実行時間

1. Excelシートの説明



   チャートを作成するプログラムの説明と動作確認をします。動作確認に使うExcelは以下のようになります。
 やりたいことは、
  1. チャートの作成
  2. チャートの削除
  3. チャートのデータ入力方法の確認
  4. チャート作成の時間
 そのために、「チャート作成」「チャート削除」のボタンを置きます。さらに、 A1~A20にRAND()で作った数値を入れておきます。これは、チャートで表示するためのデータです。




2. プログラムの説明

「チャート作成」ボタンのプログラムです。Timer関数を使って、プログラムの実行開始時間と実行終了時間を記録して、実行時間を計算します。その間にfor文でCreateChart()関数を実行します。これがチャートを作成する関数になっていて、繰り返し実行するのは実行時間を計算しやすいようにしています。最後に実行時間をM8に代入します。

Private Sub Create_Chart_Button_Click()

    Dim i As Integer
    Dim j As Integer
    Dim startTime As Variant
    Dim stopTime As Variant
    Dim exeTime As Variant

    startTime = Timer
    
     For i = 0 To 2
        For j = 0 To 50
            Call CreateChart(i, j)
        Next
    Next
    
    stopTime = Timer
    
    exeTime = stopTime - startTime
    
    Range("M8") = "実行時間は" & exeTime & "秒"

End Sub


「チャート削除」ボタンのプログラムです。シート上にあるチャートをすべて削除する関数を呼び出します。

Private Sub chart_Delete_Button_Click()

    ActiveSheet.ChartObjects.Delete

End Sub


チャートを作成する関数です。AddChart関数でチャートを追加します。引数は順番に、チャートタイプと、場所(Left, Top),サイズ(Width, Height)を指定します。 次に、SeriseCollection, NewSeriesを変数に入れます。デバッグしやすいように一つずつ変数に入れています。最後にチャートにデータを指定します。 ここで、指定する方法を3つ試しています。


  • シート上のRangeを指定して配置する方法
  • Stringで指定する方法
  • Arrayで指定する方法
Public Sub CreateChart(x_index As Integer, y_index As Integer)
    Dim size As Integer
    Dim ShapeObj As Shape
    Dim ns As Series
    Dim sc As SeriesCollection
    
    size = 200
    
    Set ShapeObj = ActiveSheet.Shapes.AddChart(xlLine, x_index * size, y_index * size, size, size)
    Set sc = ShapeObj.Chart.SeriesCollection
    Set ns = ShapeObj.Chart.SeriesCollection.NewSeries
    
'    ns.Values = Range("A1:A20")
    
'    ns.Values = "{  0.39027587  , 0.177445453 , 0.216884755 , 0.923649472 , 0.971322831 , 0.261088821 , 0.974577475 , 0.913186582 ," _
'                & " 0.366751825 , 0.311130149 , 0.179628318 , 0.3769544   , 0.710572627 , 0.370327454 , 0.491738795 , 0.604844844 ," _
'                & " 0.224992757 , 0.182349875 , 0.209868295 , 0.400079373 }"

    ns.Values = Array(0.39027587, 0.177445453, 0.216884755, 0.923649472, 0.971322831, 0.261088821, 0.974577475, 0.913186582, _
                        0.366751825, 0.311130149, 0.179628318, 0.3769544, 0.710572627, 0.370327454, 0.491738795, 0.604844844, _
                        0.224992757, 0.182349875, 0.209868295, 0.400079373)
                        
                        
End Sub



3.実行結果

 チャートを3 x 51個配置した結果です。実行時間は「0.746秒」となっていました。図を配置するという動作にはこれくらい時間がかかることがわかりました。実際にこのプログラムを応用するためには、ここから軸を追加したり、Seriesを増やしていくことになるが、実行時間を抑えるようにデバッグしながらコーディングしていきます。
 データの入力方法で実行時間が大きく変動するかを調べたかったが、あまり差はなさそうだったので、データ読み出しとの相性の良い方法を選んで使いたいと思います。



2016年5月5日木曜日

Excelシート上の座標②

1. 確認の続き

  次に、ボタンをクリックした時の動作として、以下のプログラムを実行してみます。
 プログラムとしては、セルのB2~C4にサイズを合わせてチャートを作成するものです。まず、Rangeを指定して、指定したRangeのTop, Left, Width, HeightをA列に表示して確認する。次に、指定したRangeの大きさに合わせてチャートを作成する。

Private Sub Create_chart_button_Click()

    Dim range_var As Range
   
    Set range_var = Range("B2:C4")
   
    Range("A1") = range_var.Top
    Range("A2") = range_var.Left
    Range("A3") = range_var.Width
    Range("A4") = range_var.Height
   
    Call ActiveSheet.Shapes.AddChart(Null, range_var.Left, range_var.Top, _    
    range_var.Width, range_var.Height)
   
End Sub

 実行結果は, A1 = Top, A2 = Left, A3 = Width, A4 = heightとなっています。さらに、B2~C4にチャートが生成されています。前回と同じようにチャートの中身はありません。
 Excelシートの行と列のデフォルトは、行の高さ=15, 列の幅 = 8.38となっているので、行の高さはチャートで指定している座標と一致しているみたいです。列の幅は、8.38で座標では63.75となっていて関係がよくわからなかったです。




 

 2. わかったこと

行の高さを変更した場合、例えばデフォルトの高さ15 → 10 に変更して再度プログラムを実行すると10ではなく、[9.75]になっていました。ということなので、行の高さ、列の幅を設定しておいて、その升目に合わせてチャートを正確に配置するのはむずかしい?ということなのか。。
  このルールを理解するよりも、Rangeから座標を取得して、そこに配置する方法がよさそうです。






2016年5月4日水曜日

最初の投稿

Excel, Accessでよく使う機能をメモしいくブログです。 この投稿が初めの一歩。

1.Excel VBAでプログラミングする準備


 VBAでプログラミングするには、リボンに「開発タブ」を出しておくと便利な ので、その設定をしておく。(リボンやタブという用語の使い方にいまいち慣れないので、間違っていたらすみません。)




リボンに「開発タブ」を表示させるためには、「ファイル」→「オプション」を選択して、右側の開発のチェックをONにするとメニューに「開発タブ」が表示される。























2. Visual Basic Editorを起動する。


VBAのプログラミングを記述するためのVisual Basic Editorを起動する方法は、いくつかあります。
よく使う方法としては、「開発タブ」→ 「Visual Basic Editor」を選択するか、ショートカットで(Alt + F11)があります。






Excelシート上の座標①

1. Excelシート上の座標を理解する。


MS Acess に保存してあるデータを読み出して、Excel VBAでチャートを作成するプログラムを作っているときに、セルで表示している行、列の幅と座標の関係が理解できてなかったことに気付いたので、調べてメモを残しておきます。


2. 動作確認するためのVBAプログラム


座標を確認するためのプログラムを説明します。

プログラムを実行するボタンを作成


 最初に、「デザインモード」を選択します。次に、リボンの「挿入」から「コマンドボタン」(Activex コントロール)を選択します。これで、シート上にボタンを配置できるようになっているので、シート上でクリックして、その後、ドラッグでボタンのサイズを決めます。




 デフォルトだとボタンの名前が「CommandButton1」となっているので、この文字列を変更します。
ボタン上で右クリックして、「プロパティ」を選択すると、設定を変更するためのウインドウが開きますので、「オブジェクト名」「Caption」を変更します。
「オブジェクト名」は、プログラム内でこのボタンを識別するために使用する名前なので、区別が付く名前を付けておきます。
「Caption」は、ボタンの表示名なので、ここでは、チャートを作成としておきます。







VBAのコーディング


「デザインモード」を選択した状態で、作成したボタンをダブルクリックすると、Visual Basic Editorが立ち上がって、「Create_Chart_Button_Click()」というSubが作成されます。先ほど作成したボタンをクリックすると、ここに記述したコードが実行されます。

今回は、以下のようなプログラムにしました。
Call ActiveSheet.Shapes.AddChart(Null, 100, 100, 200, 200)
詳細は、Methdの説明に書いてありますが、最初の引数は、chartTypeなので、ここではNullにしています。チャートが発生する座標を確認するだけなので、チャートタイプは、無視します。残りの4つの引数は、順番に Left, Top, width, heightとなっていて、100,100,200,200で実行するプログラムとなっています。




実行した結果は、次の図のようになる。行の高さと列の幅を確認すると行の高さは100に近い値ではあるが厳密には一致してない。列の幅は、100とはまったく違う値になっています。


その2へ。