diff options
author | Sergey Grebenshchikov <sgreben@gmail.com> | 2018-03-29 00:53:05 +0200 |
---|---|---|
committer | Sergey Grebenshchikov <sgreben@gmail.com> | 2018-03-30 01:00:44 +0200 |
commit | ef658b5e65bfef1c1ee62125ec6f0c33a27eb7e7 (patch) | |
tree | 88b265cb06e76b54c6f467b65e944457bd7c308d /cmd | |
parent | 3791de72267fc1b561e7f22595c9e90353f2f351 (diff) |
Add histogram plot type, display bar chart labels on the right instead of overlapping.
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/jp/bar.go | 33 | ||||
-rw-r--r-- | cmd/jp/hist.go | 50 | ||||
-rw-r--r-- | cmd/jp/line.go | 37 | ||||
-rw-r--r-- | cmd/jp/main.go | 13 | ||||
-rw-r--r-- | cmd/jp/scatter.go | 34 | ||||
-rw-r--r-- | cmd/jp/split.go | 6 |
6 files changed, 113 insertions, 60 deletions
diff --git a/cmd/jp/bar.go b/cmd/jp/bar.go index 15dd196..cfc39c2 100644 --- a/cmd/jp/bar.go +++ b/cmd/jp/bar.go @@ -2,37 +2,38 @@ package main import ( "fmt" + "log" "reflect" + "github.com/sgreben/jp/pkg/data" "github.com/sgreben/jp/pkg/draw" "github.com/sgreben/jp/pkg/plot" ) -func barPlotData(xvv, yvv [][]reflect.Value) (x []string, y []float64) { - for _, xv := range xvv { - for i := range xv { - if xv[i].IsValid() && xv[i].CanInterface() { - x = append(x, fmt.Sprint(xv[i].Interface())) - } +func barPlotData(xv, yv []reflect.Value) (x []string, y []float64) { + for i := range xv { + if xv[i].IsValid() && xv[i].CanInterface() { + x = append(x, fmt.Sprint(xv[i].Interface())) } } - for _, yv := range yvv { - for i := range yv { - if yv[i].IsValid() && yv[i].CanInterface() { - yvi, ok := yv[i].Interface().(float64) - if ok { - y = append(y, yvi) - } + for i := range yv { + if yv[i].IsValid() && yv[i].CanInterface() { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) } } } return } -func barPlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { - groups, y := barPlotData(xvv, yvv) +func barPlot(xv, yv []reflect.Value, c draw.Canvas) string { + groups, y := barPlotData(xv, yv) chart := plot.NewBarChart(c) - data := new(plot.DataTable) + data := new(data.Table) + if len(y) == 0 { + log.Fatal("no valid y values given") + } if len(groups) != len(y) { for i := range y { data.AddColumn(fmt.Sprint(i)) diff --git a/cmd/jp/hist.go b/cmd/jp/hist.go new file mode 100644 index 0000000..77c04e6 --- /dev/null +++ b/cmd/jp/hist.go @@ -0,0 +1,50 @@ +package main + +import ( + "log" + "reflect" + + "github.com/sgreben/jp/pkg/data" + "github.com/sgreben/jp/pkg/draw" + "github.com/sgreben/jp/pkg/plot" +) + +func histogramData(xv []reflect.Value, nbins uint) (groups []string, counts []float64) { + var x []float64 + for i := range xv { + if xv[i].IsValid() && xv[i].CanInterface() { + xvi, ok := xv[i].Interface().(float64) + if ok { + x = append(x, xvi) + } + } + } + if len(x) == 0 { + log.Fatal("no valid x values given") + } + bins := data.NewBins(x) + bins.Number = int(nbins) + if nbins == 0 { + bins.ChooseSturges() + } + hist := data.Histogram(x, bins) + groups = make([]string, len(hist)) + counts = make([]float64, len(hist)) + for i, b := range hist { + groups[i] = b.String() + counts[i] = float64(b.Count) + } + return +} + +func histogram(xv []reflect.Value, c draw.Canvas, nbins uint) string { + groups, counts := histogramData(xv, nbins) + chart := plot.NewBarChart(c) + chart.BarPaddingX = 0 + data := new(data.Table) + for _, g := range groups { + data.AddColumn(g) + } + data.AddRow(counts...) + return chart.Draw(data) +} diff --git a/cmd/jp/line.go b/cmd/jp/line.go index caca49f..8054f87 100644 --- a/cmd/jp/line.go +++ b/cmd/jp/line.go @@ -1,47 +1,48 @@ package main import ( + "log" "reflect" + "github.com/sgreben/jp/pkg/data" "github.com/sgreben/jp/pkg/draw" "github.com/sgreben/jp/pkg/plot" ) -func linePlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { - for _, xv := range xvv { - for i := range xv { - if xv[i].IsValid() && xv[i].CanInterface() { - xvi, ok := xv[i].Interface().(float64) - if ok { - x = append(x, xvi) - } +func linePlotData(xv, yv []reflect.Value) (x, y []float64) { + for i := range xv { + if xv[i].IsValid() && xv[i].CanInterface() { + xvi, ok := xv[i].Interface().(float64) + if ok { + x = append(x, xvi) } } } - for _, yv := range yvv { - for i := range yv { - if yv[i].IsValid() && yv[i].CanInterface() { - yvi, ok := yv[i].Interface().(float64) - if ok { - y = append(y, yvi) - } + for i := range yv { + if yv[i].IsValid() && yv[i].CanInterface() { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) } } } return } -func linePlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { - x, y := linePlotData(xvv, yvv) +func linePlot(xv, yv []reflect.Value, c draw.Canvas) string { + x, y := linePlotData(xv, yv) chart := plot.NewLineChart(c) - data := new(plot.DataTable) + data := new(data.Table) data.AddColumn("x") data.AddColumn("y") n := len(x) if len(y) > n { n = len(y) } + if len(y) == 0 { + log.Fatal("no valid y values given") + } // If no valid xs are given, use the indices as x values. if len(x) == 0 { x = make([]float64, len(y)) diff --git a/cmd/jp/main.go b/cmd/jp/main.go index 62f3db0..173ac27 100644 --- a/cmd/jp/main.go +++ b/cmd/jp/main.go @@ -23,6 +23,7 @@ type configuration struct { PlotType enumVar CanvasType enumVar InputType enumVar + HistBins uint } const ( @@ -51,6 +52,7 @@ var config = configuration{ plotTypeLine, plotTypeBar, plotTypeScatter, + plotTypeHist, }, }, CanvasType: enumVar{ @@ -86,8 +88,10 @@ func init() { flag.StringVar(&config.XY, "xy", "", "x,y value pairs (JSONPath expression). Overrides -x and -y if given.") flag.IntVar(&config.Box.Width, "width", 0, "Plot width (default 0 (auto))") flag.IntVar(&config.Box.Height, "height", 0, "Plot height (default 0 (auto))") + flag.UintVar(&config.HistBins, "bins", 0, "Number of histogram bins (default 0 (auto))") flag.Parse() log.SetOutput(os.Stderr) + log.SetFlags(log.LstdFlags | log.Lshortfile) var err error xPattern = jsonpath.New("x") @@ -143,12 +147,12 @@ func main() { } in = parseRows(rows) } - var x, y [][]reflect.Value + var x, y []reflect.Value if xyPattern != nil { x, y = split(match(in, xyPattern)) } else { - x = match(in, xPattern) - y = match(in, yPattern) + x = flatten(match(in, xPattern)) + y = flatten(match(in, yPattern)) } buffer := draw.NewBuffer(config.Box) var p draw.Pixels @@ -162,7 +166,6 @@ func main() { } p.Clear() c := draw.Canvas{Pixels: p} - fmt.Println() switch config.PlotType.Value { case plotTypeLine: fmt.Println(linePlot(x, y, c)) @@ -170,5 +173,7 @@ func main() { fmt.Println(scatterPlot(x, y, c)) case plotTypeBar: fmt.Println(barPlot(x, y, c)) + case plotTypeHist: + fmt.Println(histogram(x, c, config.HistBins)) } } diff --git a/cmd/jp/scatter.go b/cmd/jp/scatter.go index 9df8b4a..2fdf0e6 100644 --- a/cmd/jp/scatter.go +++ b/cmd/jp/scatter.go @@ -3,39 +3,35 @@ package main import ( "reflect" + "github.com/sgreben/jp/pkg/data" "github.com/sgreben/jp/pkg/draw" - "github.com/sgreben/jp/pkg/plot" ) -func scatterPlotData(xvv, yvv [][]reflect.Value) (x, y []float64) { - for _, xv := range xvv { - for i := range xv { - if xv[i].IsValid() && xv[i].CanInterface() { - xvi, ok := xv[i].Interface().(float64) - if ok { - x = append(x, xvi) - } +func scatterPlotData(xv, yv []reflect.Value) (x, y []float64) { + for i := range xv { + if xv[i].IsValid() && xv[i].CanInterface() { + xvi, ok := xv[i].Interface().(float64) + if ok { + x = append(x, xvi) } } } - for _, yv := range yvv { - for i := range yv { - if yv[i].IsValid() && yv[i].CanInterface() { - yvi, ok := yv[i].Interface().(float64) - if ok { - y = append(y, yvi) - } + for i := range yv { + if yv[i].IsValid() && yv[i].CanInterface() { + yvi, ok := yv[i].Interface().(float64) + if ok { + y = append(y, yvi) } } } return } -func scatterPlot(xvv, yvv [][]reflect.Value, c draw.Canvas) string { - x, y := scatterPlotData(xvv, yvv) +func scatterPlot(xv, yv []reflect.Value, c draw.Canvas) string { + x, y := scatterPlotData(xv, yv) chart := plot.NewScatterChart(c) - data := new(plot.DataTable) + data := new(data.Table) data.AddColumn("x") data.AddColumn("y") n := len(x) diff --git a/cmd/jp/split.go b/cmd/jp/split.go index 4f36825..efe59c9 100644 --- a/cmd/jp/split.go +++ b/cmd/jp/split.go @@ -11,10 +11,10 @@ func flatten(in [][]reflect.Value) (out []reflect.Value) { return } -func split(in [][]reflect.Value) (x, y [][]reflect.Value) { +func split(in [][]reflect.Value) (x, y []reflect.Value) { flat := flatten(in) n := len(flat) - x = [][]reflect.Value{flat[:n/2]} - y = [][]reflect.Value{flat[n/2:]} + x = flat[:n/2] + y = flat[n/2:] return } |