Skip to content

Commit

Permalink
Add heatmap plots
Browse files Browse the repository at this point in the history
  • Loading branch information
sgreben committed Mar 30, 2018
1 parent df8bf8e commit 0f9e84d
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = 1.1.7
VERSION = 1.1.8

APP := jp
PACKAGES := $(shell go list -f {{.Dir}} ./...)
Expand Down
52 changes: 45 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Dead simple terminal plots from JSON (or CSV) data. Bar charts, line charts, and
- [Array data, XY pairs](#array-data-xy-pairs)
- [Y values only (X=index)](#y-values-only-xindex-1)
- [Scatter plot](#scatter-plot)
- [Heatmap](#heatmap)
- [Histogram](#histogram)
- [Auto bin number](#auto-bin-number)
- [Fixed bin number](#fixed-bin-number)
Expand All @@ -38,16 +39,16 @@ Or [download the binary](https://github.com/sgreben/jp/releases/latest) from the

```bash
# Linux
curl -LO https://github.com/sgreben/jp/releases/download/1.1.7/jp_1.1.7_linux_x86_64.zip
unzip jp_1.1.7_linux_x86_64.zip
curl -LO https://github.com/sgreben/jp/releases/download/1.1.8/jp_1.1.8_linux_x86_64.zip
unzip jp_1.1.8_linux_x86_64.zip

# OS X
curl -LO https://github.com/sgreben/jp/releases/download/1.1.7/jp_1.1.7_osx_x86_64.zip
unzip jp_1.1.7_osx_x86_64.zip
curl -LO https://github.com/sgreben/jp/releases/download/1.1.8/jp_1.1.8_osx_x86_64.zip
unzip jp_1.1.8_osx_x86_64.zip

# Windows
curl -LO https://github.com/sgreben/jp/releases/download/1.1.7/jp_1.1.7_windows_x86_64.zip
unzip jp_1.1.7_windows_x86_64.zip
curl -LO https://github.com/sgreben/jp/releases/download/1.1.8/jp_1.1.8_windows_x86_64.zip
unzip jp_1.1.8_windows_x86_64.zip
```

## Use it
Expand All @@ -57,7 +58,7 @@ unzip jp_1.1.7_windows_x86_64.zip
```text
Usage of jp:
-type value
Plot type. One of [line bar scatter hist] (default line)
Plot type. One of [line bar scatter hist hist2d] (default line)
-x string
x values (JSONPath expression)
-y string
Expand Down Expand Up @@ -282,6 +283,43 @@ $ cat examples/mvrnorm.json | jp -xy '..[x,y]' -type scatter
-4.08815 3.79083
```

### Heatmap

```
$ cat examples/mvrnorm.json | jp -xy '..[x,y]' -type hist2d
3.3608│ ···· ········ ····
│ ···· ········ ····
│ ···· ········ ····
│ ················ ····
│ ················ ····
│ ································
│ ································
│ ················░░░░░░░░░░░░················
│ ················░░░░░░░░░░░░················
│ ············▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░░░░············
│ ············▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░░░░············
│···············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒············
│···············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒················
│ ············▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒················
│ ····································
│ ····································
│ ····························
│ ····························
│ ···· ···· ····
│ ···· ···· ····
│ ····
│ ····
-4.0045└───────────────────────────────────────────────────
-3.8421 3.5909
```

### Histogram

#### Auto bin number
Expand Down
40 changes: 39 additions & 1 deletion README.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Dead simple terminal plots from JSON (or CSV) data. Bar charts, line charts, and
- [Array data, XY pairs](#array-data-xy-pairs)
- [Y values only (X=index)](#y-values-only-xindex-1)
- [Scatter plot](#scatter-plot)
- [Heatmap](#heatmap)
- [Histogram](#histogram)
- [Auto bin number](#auto-bin-number)
- [Fixed bin number](#fixed-bin-number)
Expand Down Expand Up @@ -57,7 +58,7 @@ unzip jp_${VERSION}_windows_x86_64.zip
```text
Usage of jp:
-type value
Plot type. One of [line bar scatter hist] (default line)
Plot type. One of [line bar scatter hist hist2d] (default line)
-x string
x values (JSONPath expression)
-y string
Expand Down Expand Up @@ -282,6 +283,43 @@ $ cat examples/mvrnorm.json | jp -xy '..[x,y]' -type scatter
-4.08815 3.79083
```

### Heatmap

```
$ cat examples/mvrnorm.json | jp -xy '..[x,y]' -type hist2d
3.3608│ ···· ········ ····
│ ···· ········ ····
│ ···· ········ ····
│ ················ ····
│ ················ ····
│ ································
│ ································
│ ················░░░░░░░░░░░░················
│ ················░░░░░░░░░░░░················
│ ············▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░░░░············
│ ············▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░░░░············
│···············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒············
│···············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ········░░░░▓▓▓▓▓▓▓▓████▓▓▓▓▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒············
│ ············▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒················
│ ············▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒················
│ ····································
│ ····································
│ ····························
│ ····························
│ ···· ···· ····
│ ···· ···· ····
│ ····
│ ····
-4.0045└───────────────────────────────────────────────────
-3.8421 3.5909
```

### Histogram

#### Auto bin number
Expand Down
1 change: 1 addition & 0 deletions cmd/jp/canvas_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ var autoCanvas = map[string]string{
plotTypeLine: canvasTypeQuarter,
plotTypeScatter: canvasTypeBraille,
plotTypeHist: canvasTypeQuarter,
plotTypeHeatmap: canvasTypeFull,
}
1 change: 1 addition & 0 deletions cmd/jp/canvas_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ var autoCanvas = map[string]string{
plotTypeLine: canvasTypeFull,
plotTypeScatter: canvasTypeFull,
plotTypeHist: canvasTypeFull,
plotTypeHeatmap: canvasTypeFull,
}
55 changes: 55 additions & 0 deletions cmd/jp/heatmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"log"
"reflect"

"github.com/sgreben/jp/pkg/data"
"github.com/sgreben/jp/pkg/draw"
"github.com/sgreben/jp/pkg/plot"
)

func heatmapData(xv []reflect.Value, yv []reflect.Value, nbins uint) (heatmap *data.Heatmap) {
var 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 i := range yv {
if yv[i].IsValid() && yv[i].CanInterface() {
yvi, ok := yv[i].Interface().(float64)
if ok {
y = append(y, yvi)
}
}
}
if len(x) != len(y) {
log.Fatal(len(x), " = len(x) != len(y) = ", len(y))
}
points := make([][2]float64, len(x))
for i := 0; i < len(x); i++ {
points[i] = [2]float64{x[i], y[i]}
}
if len(x) == 0 {
log.Fatal("no valid x values given")
}
bins := data.NewBins2D(points)
bins.X.Number = int(nbins)
bins.Y.Number = int(nbins)
if nbins == 0 {
bins.X.Number = data.BinsSturges(len(points))
bins.Y.Number = data.BinsSturges(len(points))
}
heatmap = data.NewHeatmap(data.Histogram2D(points, bins))
return
}

func heatmap(xv, yv []reflect.Value, c draw.Canvas, nbins uint) string {
heatmap := heatmapData(xv, yv, nbins)
chart := plot.NewHeatMap(c.GetBuffer())
return chart.Draw(heatmap)
}
2 changes: 1 addition & 1 deletion cmd/jp/hist.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func histogramData(xv []reflect.Value, nbins uint) (groups []string, counts []fl
bins := data.NewBins(x)
bins.Number = int(nbins)
if nbins == 0 {
bins.ChooseSturges()
bins.Number = data.BinsSturges(len(x))
}
hist := data.Histogram(x, bins)
groups = make([]string, len(hist))
Expand Down
13 changes: 11 additions & 2 deletions cmd/jp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
plotTypeBar = "bar"
plotTypeScatter = "scatter"
plotTypeHist = "hist"
plotTypeHeatmap = "hist2d"
)

const (
Expand All @@ -53,6 +54,7 @@ var config = configuration{
plotTypeBar,
plotTypeScatter,
plotTypeHist,
plotTypeHeatmap,
},
},
CanvasType: enumVar{
Expand Down Expand Up @@ -123,6 +125,11 @@ func init() {
}

func match(in interface{}, p *jsonpath.JSONPath) [][]reflect.Value {
defer func() {
if r := recover(); r != nil {
log.Println("error evaluating JSONPath", p.String+":", r)
}
}()
out, err := p.FindResults(in)
if err != nil {
log.Println(err)
Expand All @@ -137,13 +144,13 @@ func main() {
dec := json.NewDecoder(os.Stdin)
err := dec.Decode(&in)
if err != nil {
log.Println(err)
log.Println(inputTypeJSON, "input:", err)
}
case inputTypeCSV:
r := csv.NewReader(os.Stdin)
rows, err := r.ReadAll()
if err != nil {
log.Println(err)
log.Println(inputTypeCSV, "input:", err)
}
in = parseRows(rows)
}
Expand Down Expand Up @@ -175,5 +182,7 @@ func main() {
fmt.Println(barPlot(x, y, c))
case plotTypeHist:
fmt.Println(histogram(x, c, config.HistBins))
case plotTypeHeatmap:
fmt.Println(heatmap(x, y, c, config.HistBins))
}
}
68 changes: 68 additions & 0 deletions pkg/data/heatmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package data

import "math"

type Heatmap struct {
X, Y []Bin
Z [][]float64
MinX, MaxX uint64
MinY, MaxY uint64
MinZ, MaxZ uint64
}

func NewHeatmap(x, y []Bin, z [][]uint64) *Heatmap {
h := new(Heatmap)
h.X, h.Y = x, y
h.Z = make([][]float64, len(z))
h.MinX, h.MinY, h.MinZ = math.MaxUint64, math.MaxUint64, math.MaxUint64
h.MaxX, h.MaxY, h.MaxZ = 0, 0, 0
for _, b := range x {
if b.Count > h.MaxX {
h.MaxX = b.Count
}
if b.Count < h.MinX {
h.MinX = b.Count
}
}
for _, b := range x {
b.CountNorm = float64(b.Count-h.MinX) / float64(h.MaxX-h.MinX)
}
for _, b := range y {
if b.Count > h.MaxY {
h.MaxY = b.Count
}
if b.Count < h.MinY {
h.MinY = b.Count
}
}
for _, b := range y {
b.CountNorm = float64(b.Count-h.MinY) / float64(h.MaxY-h.MinY)
}
for i := range z {
h.Z[i] = make([]float64, len(z[i]))
for _, b := range z[i] {
if b > h.MaxZ {
h.MaxZ = b
}
if b < h.MinZ {
h.MinZ = b
}
}
}
for i := range z {
for j := range z[i] {
h.Z[i][j] = float64(z[i][j]-h.MinZ) / float64(h.MaxZ-h.MinZ)
}
}
if h.MaxX == 0 {
h.MaxX = 1
}
if h.MaxY == 0 {
h.MaxY = 1
}
if h.MaxZ == 0 {
h.MaxZ = 1
}

return h
}
Loading

0 comments on commit 0f9e84d

Please sign in to comment.