スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門 他のアプリとの連携編 その4 Excel(4)

今回はグラフ化に挑戦してみたいと思います。
 
以下のコードをF#Interactiveに食わせている状態から説明を加えます。
 
#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif
 
open Microsoft.Office.Interop.Excel 
  
let set (ws:Worksheet) (row:int) (col:int) v =
    ws.Cells.Item(row,col) <- v
 
let setH (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow,sCol + i ,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v)
 
let setV (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow+i,sCol,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v) 
  
let app = new ApplicationClass(Visible = true)
 
let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
let worksheet = workbook.Worksheets.[1] :?> Worksheet
 
setH worksheet 2 2 ["year";"2008";"2009";"2010"]
setH worksheet 3 3 [1234.0; 1240.0; 1100.0]
setH worksheet 4 3 [605.0; 580.0; 550.0]
setH worksheet 5 3 [991.0; 1050.0; 1009.0]
setH worksheet 6 3 [777.0; 508.0; 603.0]
setH worksheet 7 3 [888.0; 896.0; 810.0]
setV worksheet 3 2 ["東京";"長野";"大阪";"京都";"千葉"] 
 
704-1.jpg 
 
まずチャートオブジェツを定義します。
 
let chartobjs = (worksheet.ChartObjects() :?> ChartObjects)
 
追加するチャートの位置大きさをまずは次のように定義しておきます。
 
let (g_xPos,g_yPos,g_width,g_height) = (300.0,20.0,300.0,200.0)
 
チャートオブジェクトを定義します。
 
let chartobj = chartobjs.Add(g_xPos,g_yPos,g_width,g_height)
 
次のようにChartWizardメソッドを実行するとグラフが表示されます。
 
chartobj.Chart.ChartWizard
    (Title = "サンプル",
     Source = worksheet.Range("B2","E7"),
     Gallery = XlChartType.xl3DColumn,
     PlotBy = XlRowCol.xlColumns,
     SeriesLabels = 1,
     CategoryLabels = 1,
     CategoryTitle = "",
     ValueTitle = "sample") 
      
 
 704-2.jpg


> chartobj.Chart.ChartStyle <- 1;;
val it : unit = ()
 
> chartobj.Chart.ChartStyle <- 2;;
val it : unit = ()
.....
としていくと、順次グラフの色が変わっていきます。
 
> chartobj.Delete() |> ignore;;
val it : unit = ()
 
とするとグラフが消えます。
 
次にChartWizardメソッドの引数であるGalleryを少し調べてみたいと思います。
これは内部的にenum型なので、GetNamesメソッドで、すべての名前を得ることができます。
 
> XlChartType.GetValues (typeof<Microsoft.Office.Interop.Excel.XlChartType>);;
val it : System.Array =
  [|xlArea; xlLine; xlPie; xlBubble; xlColumnClustered; xlColumnStacked;
    xlColumnStacked100; xl3DColumnClustered; xl3DColumnStacked;
    xl3DColumnStacked100; xlBarClustered; xlBarStacked; xlBarStacked100;
    xl3DBarClustered; xl3DBarStacked; xl3DBarStacked100; xlLineStacked;
    xlLineStacked100; xlLineMarkers; xlLineMarkersStacked;
    xlLineMarkersStacked100; xlPieOfPie; xlPieExploded; xl3DPieExploded;
    xlBarOfPie; xlXYScatterSmooth; xlXYScatterSmoothNoMarkers;
    xlXYScatterLines; xlXYScatterLinesNoMarkers; xlAreaStacked;
    xlAreaStacked100; xl3DAreaStacked; xl3DAreaStacked100; xlDoughnutExploded;
    xlRadarMarkers; xlRadarFilled; xlSurface; xlSurfaceWireframe;
    xlSurfaceTopView; xlSurfaceTopViewWireframe; xlBubble3DEffect; xlStockHLC;
    xlStockOHLC; xlStockVHLC; xlStockVOHLC; xlCylinderColClustered;
    xlCylinderColStacked; xlCylinderColStacked100; xlCylinderBarClustered;
    xlCylinderBarStacked; xlCylinderBarStacked100; xlCylinderCol;
    xlConeColClustered; xlConeColStacked; xlConeColStacked100;
    xlConeBarClustered; xlConeBarStacked; xlConeBarStacked100; xlConeCol;
    xlPyramidColClustered; xlPyramidColStacked; xlPyramidColStacked100;
    xlPyramidBarClustered; xlPyramidBarStacked; xlPyramidBarStacked100;
    xlPyramidCol; xlXYScatter; xlRadar; xlDoughnut; xl3DPie; xl3DLine;
    xl3DColumn; xl3DArea|]

 
垂直方向の位置とGalleryの値(XlChartType型)とタイトルを与えるとグラフを表示する関数を定義してみます。
 
let drawG (yPos:float ,gall:XlChartType,title:string)=
    let chartobj = chartobjs.Add(g_xPos,yPos,g_width,g_height)
    try    
        chartobj.Chart.ChartWizard
            (Title = title,
             Source = worksheet.Range("B2","E7"),
             Gallery = gall,
             PlotBy = XlRowCol.xlColumns,
             SeriesLabels = 1,
             CategoryLabels = 1,
             CategoryTitle = "",
             ValueTitle = "sample")
    with
       | ex -> printfn "type = %A  mes = %A " (ex.GetType ()) ex.Message 
       
なお、表示範囲の設定がGalleryの値に対しては無効な場合があり、その場合例外が投げられるので、Try withを使っています。
 
それでは、ここまでを全部まとめて、表示可能なグラフを全部表示するコードを示します。
 
#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif
 
open Microsoft.Office.Interop.Excel
 
 
let set (ws:Worksheet) (row:int) (col:int) v =
    ws.Cells.Item(row,col) <- v
 
let setH (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow,sCol + i ,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v)
 
let setV (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow+i,sCol,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v)
 
 
let app = new ApplicationClass(Visible = true)
 
let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
let worksheet = workbook.Worksheets.[1] :?> Worksheet
 
setH worksheet 2 2 ["year";"2008";"2009";"2010"]
setH worksheet 3 3 [1234.0; 1240.0; 1100.0]
setH worksheet 4 3 [605.0; 580.0; 550.0]
setH worksheet 5 3 [991.0; 1050.0; 1009.0]
setH worksheet 6 3 [777.0; 508.0; 603.0]
setH worksheet 7 3 [888.0; 896.0; 810.0]
setV worksheet 3 2 ["東京";"長野";"大阪";"京都";"千葉"]
 
let chartobjs = (worksheet.ChartObjects() :?> ChartObjects)
let (g_xPos,g_yPos,g_width,g_height) = (300.0,20.0,300.0,200.0)
 
let drawG (yPos:float ,gall:XlChartType,title:string)=
    let chartobj = chartobjs.Add(g_xPos,yPos,g_width,g_height)
    try    
        chartobj.Chart.ChartWizard
            (Title = title,
             Source = worksheet.Range("B2","E7"),
             Gallery = gall,
             PlotBy = XlRowCol.xlColumns,
             SeriesLabels = 1,
             CategoryLabels = 1,
             CategoryTitle = "",
             ValueTitle = "sample")
 
    with
       | ex -> printfn "type = %A  mes = %A " title ex.Message 
 
let cTypes =XlChartType.GetValues (typeof<Microsoft.Office.Interop.Excel.XlChartType>)
let cTypesWithIndex = Array.mapi(fun i v -> (i,v))  (cTypes :?> XlChartType []) 
 
cTypesWithIndex 
|> Array.map(fun (i,v) -> (g_yPos+(float i)*(g_height+10.0),v,v.ToString()))
|> Array.map (fun x -> drawG x)
|> ignore
 
これをF#Interactiveに送ると、次のように表示されます。
 
(表示(一部のみ)) 
 
704-3.jpg  

(表示できないグラフの部分は空箱が表示されます。)
 
> chartobjs.Delete() |> ignore;;
val it : unit = ()
 
とすればグラフが全部消えます。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

F#入門 他のアプリとの連携編 その3 Excel(3)

今回はエクセルからの値の読み込みを紹介します。
 
前回と同様でコードの最初で次のようになっているものとします。
 
#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif
 
open Microsoft.Office.Interop.Excel
 
let app = new ApplicationClass(Visible = true)
 
let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
let worksheet = workbook.Worksheets.[1] :?> Worksheet
 
このworksheet のC5に3が入っているものとします。
 
これを読みだすには次のようにします。
 
> let rv = (worksheet.Cells.Item(3,5) :?> Range).Value2;;
val rv : obj = 3.0
 
なにも入っていない場所をしていするとnullが返ります。
 
> let rv = (worksheet.Cells.Item(2,5) :?> Range).Value2;;
val rv : obj = null
 
それでは読み込みを関数化しておきます。
 
> let get (ws:Worksheet) (row : int) (col :int) =
    (ws.Cells.Item(row,col) :?> Range).Value2;;
 
val get : Worksheet -> int -> int -> obj
 
使ってみます。
> get worksheet 3 5;;
val it : obj = 3.0
 
読み込んだ値のキャストも含めて関数を定義してみます。
 
> let getFloat (ws:Worksheet) (row : int) (col :int) =
    (get ws row col) :?> float
 
let getInt (ws:Worksheet) (row : int) (col :int) =
    (get ws row col) :?> int
 
let getString (ws:Worksheet) (row : int) (col :int) =
    (get ws row col) :?> string;;
 
val getFloat : Worksheet -> int -> int -> float
val getInt : Worksheet -> int -> int -> int
val getString : Worksheet -> int -> int -> string
 
> getFloat worksheet 3 5;;
val it : float = 3.0
 
ただし違った型でキャストしようとすると例外が発生します。
 
> getInt worksheet 3 5;;
System.InvalidCastException: Specified cast is not valid.
   at <StartupCode$FSI_0011>.$FSI_0011.main@()
Stopped due to error
 
ジェネティクな関数として定義すると次のようになります。
 
> let getG<'a> (ws:Worksheet) (row : int) (col :int) =
    (get ws row col) :?> 'a;;
 
val getG : Worksheet -> int -> int -> 'a
 
> getG<float> worksheet 3 5;;
val it : float = 3.0
 
それでは範囲指定をして、その範囲の値をArray2Dに入れて返す関数を定義してみます。
 
> let getGA1<'a> (ws:Worksheet) (sR,sC) (eR,eC) =
    Array2D.init (eR - sR + 1) (eC - sC + 1) (fun r c -> getG<'a> ws (sR+r) (sC+c));;
 
val getGA1 : Worksheet -> int * int -> int * int -> 'a [,]
 
エクセルが次のような状態になっているとします。 
 

703-1.jpg 
 
(実行例)
> getGA1<float> worksheet (1,1) (2,4);;
val it : float [,] = [[4.0; 7.0; 9.0; 12.0]
                      [7.0; 3.0; 5.0; 7.0]]
                      
 
取り込む範囲を左上と右下のアドレスで指定できる関数も定義してみます。
 
let getGA2<'a> (ws:Worksheet) (sA:string) (eA:string) =
     getGA1<'a> ws (nToRC sA) (nToRC eA)
 
なおnToRCは前々回に定義した関数です。
 
(実行例)
> getGA2<float> worksheet "A1" "D2";;
val it : float [,] = [[4.0; 7.0; 9.0; 12.0]
                      [7.0; 3.0; 5.0; 7.0]]
                      
 
最後に取り込む範囲を左上のアドレスと、行数、列数で指定する関数を定義してみます。
 
> let getGA3<'a> (ws:Worksheet) (sA:string) rc cc =
    let (sR,sC) = nToRC sA
    getGA1<'a> ws (sR,sC) (sR+rc-1,sC+cc-1);;
 
val getGA3 : Worksheet -> string -> int -> int -> 'a [,]
 
(実行例)
> getGA3<float> worksheet "A1" 2 4;;
val it : float [,] = [[4.0; 7.0; 9.0; 12.0]
                      [7.0; 3.0; 5.0; 7.0]]

テーマ : プログラミング
ジャンル : コンピュータ

F#入門 他のアプリとの連携編 そのExcel(2)

今回はrangeを使っての書き込みを紹介します。

前回と同様に次のようにしておきます。 

#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif

open Microsoft.Office.Interop.Excel

let app = new ApplicationClass(Visible = true)

let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
let worksheet = workbook.Worksheets.[1] :?> Worksheet

c2セルに5と書き込むには次のようにします。

> worksheet.Range("C2").Value2 <- 5;;
val it : unit = ()

c3からF3まで 1 2 3と書き込むには次のようにします。

> worksheet.Range("C3","E3").Value2 <- [|1;2;3|];;
val it : unit = ()

長方形の領域に書き込むにはArray2Dを利用して例えば次のようにします。

例として次のような配列を準備します。

> let arr2d = Array2D.init 2 3 (fun i j -> sprintf "row:%d col:%d" i j );;

val arr2d : string [,] = [["row:0 col:0"; "row:0 col:1"; "row:0 col:2"]
                          ["row:1 col:0"; "row:1 col:1"; "row:1 col:2"]]
                         
> worksheet.Range("C2","E3").Value2 <- arr2d;;
val it : unit = ()

とすると


702-1.jpg

となります。

上のメソッドでは左上と右下の位置を両方とも指定しなければならないとダメなので、左上と配列だけで書き込めるような関数を定義してみます。

> let setArr (ws:Worksheet) (nTag : string) (arr2d: 'a[,]) =
    let (r,c) = nToRC nTag
    let rowEnd = r + (Array2D.length1 arr2d) - 1
    let colEnd = c + (Array2D.length2 arr2d) - 1
    let endNTag = (rToN colEnd) + rowEnd.ToString()
    ws.Range(nTag,endNTag).Value2 <- arr2d;;

val setArr : Worksheet -> string -> 'a [,] -> unit

なおnToRCとrToNは前回定義した関数です。

これで
> setArr worksheet "C3" arr2d;;
val it : unit = ()
とすると
worksheet.Range("C2","E3").Value2 <- arr2d
と同じ結果が得られます。

テーマ : プログラミング
ジャンル : コンピュータ

F#入門 他のアプリとの連携編 その1 Excel(1)

今回はExcelとの連携を紹介したいと思います。
 
まず、新しいソリューションで、新しいプロジェクトを作成しておきます。
(今回はF#コンソールアプリケーションとして作成してみました。)
まずは参照を追加します。
F#Interactiveでも利用したいので、ソースの先頭にを次のようにします。
 
#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif
 
open Microsoft.Office.Interop.Excel
 
上の部分をALT+retrunでF# Interactiveに送ると
 

 
--> Referenced 'C:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\office.dll'
 
--> Referenced 'C:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll'
 
と表示されます。
 
また、solution explorer側でも、参照の追加を利用して2つのDLLに参照を追加しておきます。
 
次にApplicationClassのインスタンスを生成して、appという識別子に束縛しておきます。
 
let app = new ApplicationClass(Visible = true)
 
この一行をF# Interactiveに送ると、workbookなしのExcelが起動されます。
 
次に、workbookを追加して、それをworkbookという識別子に束縛します。
 
let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
 
XlWBATemplateというのは、作成するworkbookのタイプを指定する為の型です。
 
上の一行をF# Interactiveに送ると、excelにworkbookが追加されます。
(ExcelをF#InteractiveからInteractiveに操作できる訳で、結構新鮮です。)
 
次に一枚目のsheetをworksheetという識別子に束縛します。
 
> let worksheet = workbook.Worksheets.[1];;
val worksheet : obj
 
見ての通り、これではobj型になるので、Worksheet型へキャストが必要です。
次のようにします。
 
let worksheet = workbook.Worksheets.[1] :?> Worksheet
 
これで準備が整いましたので、F#側からExcelのシートに値を書き込んでみたいと思います。
 
まずはセル単位で書き込む方法です。
 
セルの座標は左上を(1,1)として、(行row,列col)で考えます。
つまりc7セルはrow = 7,col = 3となります。
c7セルに値10を書き込むには次のようになります。
 
worksheet.Cells.Item(7,3) <- 10
 
これでc7セルの内容が10となります。
セルの値はobj型なので
worksheet.Cells.Item(7,3) <- box 10
としても同等です。
文字列も同様に書きこめます。
 
worksheet.Cells.Item(6,3) <- "Hello Excel"
 
helper関数として次のようなものを定義しておいてもよいかと思います。
 
> let set (ws:Worksheet) (row:int) (col:int) v =
    ws.Cells.Item(row,col) <- v;;
 
val set : Worksheet -> int -> int -> 'a -> unit
 
>  set worksheet 1 2 100;;
val it : unit = ()
 
でB1セルの値が100になります。
 
次に開始する行と列とseq型の値を指定すると、水平方向に書き込む関数を上のsetを利用して定義してみます。
 
> let setH (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow,sCol + i ,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v);;
 
val setH : Worksheet -> int -> int -> seq<'a> -> unit
 
 
> setH worksheet 2 1 [1;2;3;4;5];;
val it : unit = ()
 
とするとA1セルに1、B1セルに2、C1セルに3が書き込まれます。
 
同様に垂直方向に書き込む関数も定義してみます。
 
> let setV (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow+i,sCol,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v);;
 
val setV : Worksheet -> int -> int -> seq<'a> -> unit
 
 
列はA,B,C,D...Z,AA,AB,...という形式になっているので、
列(整数)-> 列名 という変換をする関数rToNという関数を定義してみます。
 
> let rToN (n :int) =
 
    let charArr = [|for c in 'A' .. 'Z' -> c|]
 
    let rec rToNSub rem result = //26進数もどきへの変換、0(="A")は[0],1(="B")は[1],26(="AA")は[0;0]となる
                                 
        if rem <= 25 then
            rem :: result
        else
            let red = rem % 26
            rToNSub ((rem - red)/26 - 1 ) (red :: result) // -1 がミソ 
    
    let makeUpCharArr =
        (rToNSub (n-1) []) // -1 は列が1から始まる為
        |>  List.map (fun i -> charArr.[i])
        |> Array.ofList
     
    new string(makeUpCharArr) ;;
 
val rToN : int -> string
 
(実行例)
 
> rToN 26;;
val it : string = "Z"
> rToN 27;;
val it : string = "AA"
 
これを利用して
setH2 "E2" [1;2;3]でE2セルから水平方向へ、setV2 "E2" [1;2;3]で垂直方向へ値が書き込まれるように関数setH2とsetV2を定義します。
 
準備としてアルファベットを数値に対応させるmapを作っておきます。
 
> let myMap = 
    [|for c in 'A' .. 'Z' -> c|]
    |> Array.mapi (fun i c -> (c,i))
    |> Map.ofArray;;
 
val myMap : Map<char,int> =
  map
    [('A', 0); ('B', 1); ('C', 2); ('D', 3); ('E', 4); ('F', 5); ('G', 6);
     ('H', 7); ('I', 8); ...]
 
    
 
これを利用して次のように定義します。
 
> let nToRC (ntag : string) =
 
    let rec nToRCSub (rem:list<char>) resR resC   =
        match rem with
        |[] -> 
            (resR,resC)
        |h :: tl when System.Char.IsDigit h ->
            nToRCSub tl (10 * resR + System.Int32.Parse(h.ToString())) resC
        |h :: tl -> 
            let num = myMap.[(System.Char.ToUpper h)]
            nToRCSub tl resR (26*(resC+1) + num)
    
    let (r,c) = (nToRCSub (List.ofArray (ntag.ToCharArray())) 0 -1)
    (r,c+1);;
 
val nToRC : string -> int * int
 
(実行例)
> nToRC "Z2";;
val it : int * int = (2, 26)
 
> nToRC "AA12";;
val it : int * int = (12, 27)
 
小文字でも大丈夫です。
> nToRC "aa12";;
val it : int * int = (12, 27)
 
それではsetH2とsetV2を定義します。
 
> let setH2 (ws:Worksheet) nTag sq =
    let (r,c) = nToRC nTag
    setH ws r c sq
 
let setV2 (ws:Worksheet) nTag sq =
    let (r,c) = nToRC nTag
    setV ws r c sq;;
 
val setH2 : Worksheet -> string -> seq<'a> -> unit
val setV2 : Worksheet -> string -> seq<'a> -> unit
 
 
(実行例)
> setH2 worksheet "C4" ["This";"is";"a";"pen"];;
val it : unit = ()
で 
 
701-1.jpg  
 
となります。

テーマ : プログラミング
ジャンル : コンピュータ

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。