LoginSignup
7
6

More than 5 years have passed since last update.

Go 言語でCSVをドラッグアンドドロップして簡単収益シュミレーション

Last updated at Posted at 2014-12-18

ソース or 実行ファイルダウンロード

[github]
https://github.com/kuxuxun/go_lang_calculation_sample

[実行ファイル]
https://raw.githubusercontent.com/kuxuxun/go_lang_calculation_sample/master/calc_all_win.zip

何?

なにかサービス作ろうとしたとき、ユーザ数とか課金率とかのパターンとか いくつか出して
全パターン掛け算してなんとなくそれぞれのリターンがいくらになるか見たいじゃないですか。

いかにもエクセルな感じのお仕事ですけど、 われわれはエンジニアなのでエクセルとか可能な限り開きたくないわけじゃないですか。
なのでCSVドラッグアンドドロップすると全パターンの掛け算して吐き出すだけのスクリプトをGoでちゃちゃっと作りました。

例えば

想定ユーザ数 想定課金率 想定ARPPU
1000 0.01 1000
10000 0.3 2000
50000

というデータをCSVで用意してexeにドラッグアンドドロップすると

想定ユーザ数 想定課金率 想定ARPPU 計算結果
1000.0000 0.0100 1000.0000 10000.0000
1000.0000 0.0100 2000.0000 20000.0000
1000.0000 0.3000 1000.0000 300000.0000
1000.0000 0.3000 2000.0000 600000.0000
10000.0000 0.0100 1000.0000 100000.0000
10000.0000 0.0100 2000.0000 200000.0000
10000.0000 0.3000 1000.0000 3000000.0000
10000.0000 0.3000 2000.0000 6000000.0000
50000.0000 0.0100 1000.0000 500000.0000
... ... ... ...

といった感じのファイルを吐き出します。

コード


package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"

    "code.google.com/p/go.text/encoding/japanese"
    "code.google.com/p/go.text/transform"
)

func noErrorOrPanic(err error) {
    if err != nil {
        panic(err)
    }
}

func validate(records [][]string) {
    if len(records) == 0 {
        panic("ファイルが空っすね")
    }
}

func main() {
    if len(os.Args) < 2 {
        panic("ファイルを指定してください")
    }
    file, err := os.Open(os.Args[1])

    noErrorOrPanic(err)
    defer file.Close()

    reader := csv.NewReader(transform.NewReader(file, japanese.ShiftJIS.NewDecoder()))

    var records [][]string
    for {
        record, err := reader.Read()
        if err == io.EOF {
            break
        } else {
            noErrorOrPanic(err)
        }

        records = append(records, record)
    }

    validate(records)
    header, data := records[:1][0], records[1:]

    // 転置
    var vs [][]float64 = make([][]float64, len(header))
    for _, row := range data {
        for col := 0; col < len(header); col++ {
            if v, err := strconv.ParseFloat(strings.Trim(row[col], " "), 64); err == nil {
                vs[col] = append(vs[col], v)
            }
        }
    }

    output, err := os.Create(file.Name() + ".calc.csv")
    defer output.Close()
    writer := csv.NewWriter(transform.NewWriter(output, japanese.ShiftJIS.NewEncoder()))

    writer.Write(append(header, "計算結果"))

    toStrs := func(nums []float64) []string {
        ss := []string{}
        for _, n := range nums {
            ss = append(ss, fmt.Sprintf("%.4f", n))
        }
        return ss
    }

    multipleAll := func(nums []float64) {
        r := 1.0

        for _, n := range nums {
            r = r * n
        }
        writer.Write(toStrs(append(nums, r)))
    }

    var d []float64
    walk(vs, d, multipleAll)

    writer.Flush()
}

func walk(datas [][]float64, currentDatas []float64, proc func([]float64)) {
    currentLevelDatas, nextLevelDatas := datas[:1][0], datas[1:]
    if len(nextLevelDatas) == 0 {
        for _, each := range currentLevelDatas {
            ds := append(currentDatas, each)
            proc(ds)
        }
        return
    }

    for _, each := range currentLevelDatas {
        ds := append(currentDatas, each)
        walk(nextLevelDatas, ds, proc)
    }

}

感想

営業さんやマーケターの方が何かお困りで、何か作ってあげたい、となった時、
Goだとexeにコンパイルしてちゃちゃっとばら撒けるので楽ですね。

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6