func VisualizeSignificantEvents(events SignificantEvents, filename string, options SignificantEventsOptions) {
	firstTime := events.FirstTime()
	tr := func(t time.Time) float64 {
		return t.Sub(firstTime).Seconds()
	}

	minX := options.MinX
	maxX := 0.0
	maxY := 0.0
	minY := math.MaxFloat64

	histograms := map[string]plot.Plotter{}
	scatters := map[string][]plot.Plotter{}
	verticalLines := []plot.Plotter{}

	colorCounter := 0
	for _, name := range events.OrderedNames() {
		xys := plotter.XYs{}
		xErrs := plotter.XErrors{}
		for _, event := range events[name] {
			if event.V > 0 {
				xys = append(xys, struct{ X, Y float64 }{tr(event.T), event.V})
				xErrs = append(xErrs, struct{ Low, High float64 }{-event.V / 2, event.V / 2})

				if tr(event.T) > maxX {
					maxX = tr(event.T)
				}
				if event.V > maxY {
					maxY = event.V
				}
				if event.V < minY {
					minY = event.V
				}
			}
		}

		if len(xys) == 0 {
			say.Println(0, "No data for %s", name)
			continue
		}

		if options.MarkedEvents != nil {
			ls, ok := options.MarkedEvents[name]
			if ok {
				for _, event := range events[name] {
					l := viz.NewVerticalLine(tr(event.T))
					l.LineStyle = ls
					verticalLines = append(verticalLines, l)
				}
			}
		}

		s, err := plotter.NewScatter(xys)
		say.ExitIfError("Couldn't create scatter plot", err)

		s.GlyphStyle = plot.GlyphStyle{
			Color:  viz.OrderedColor(colorCounter),
			Radius: 2,
			Shape:  plot.CircleGlyph{},
		}

		xErrsPlot, err := plotter.NewXErrorBars(struct {
			plotter.XYer
			plotter.XErrorer
		}{xys, xErrs})
		say.ExitIfError("Couldn't create x errors plot", err)
		xErrsPlot.LineStyle = viz.LineStyle(viz.OrderedColor(colorCounter), 1)

		scatters[name] = []plot.Plotter{s, xErrsPlot}

		durations := events[name].Data()
		h := viz.NewHistogram(durations, 20, durations.Min(), durations.Max())
		h.LineStyle = viz.LineStyle(viz.OrderedColor(colorCounter), 1)
		histograms[name] = h
		colorCounter++
	}

	if options.MaxX != 0 {
		maxX = options.MaxX
	}

	maxY = math.Pow(10, math.Ceil(math.Log10(maxY)))
	minY = math.Pow(10, math.Floor(math.Log10(minY)))

	b := &viz.Board{}
	n := len(histograms) + 1
	padding := 0.1 / float64(n-1)
	height := (1.0 - padding*float64(n-1)) / float64(n)
	histWidth := 0.3
	scatterWidth := 0.7
	y := 1 - height - padding - height

	allScatterPlot, _ := plot.New()
	allScatterPlot.Title.Text = "All Events"
	allScatterPlot.X.Label.Text = "Time (s)"
	allScatterPlot.Y.Label.Text = "Duration (s)"
	allScatterPlot.Y.Scale = plot.LogScale
	allScatterPlot.Y.Tick.Marker = plot.LogTicks

	for _, name := range events.OrderedNames() {
		histogram, ok := histograms[name]
		if !ok {
			continue
		}
		scatter := scatters[name]

		allScatterPlot.Add(scatter[0])
		allScatterPlot.Add(scatter[1])

		histogramPlot, _ := plot.New()
		histogramPlot.Title.Text = name
		histogramPlot.X.Label.Text = "Duration (s)"
		histogramPlot.Y.Label.Text = "N"
		histogramPlot.Add(histogram)

		scatterPlot, _ := plot.New()
		scatterPlot.Title.Text = name
		scatterPlot.X.Label.Text = "Time (s)"
		scatterPlot.Y.Label.Text = "Duration (s)"
		scatterPlot.Y.Scale = plot.LogScale
		scatterPlot.Y.Tick.Marker = plot.LogTicks
		scatterPlot.Add(scatter...)
		scatterPlot.Add(verticalLines...)
		scatterPlot.X.Min = minX
		scatterPlot.X.Max = maxX
		scatterPlot.Y.Min = 1e-5
		scatterPlot.Y.Max = maxY

		b.AddSubPlot(histogramPlot, viz.Rect{0, y, histWidth, height})
		b.AddSubPlot(scatterPlot, viz.Rect{histWidth, y, scatterWidth, height})

		y -= height + padding
	}

	allScatterPlot.Add(verticalLines...)
	allScatterPlot.X.Min = minX
	allScatterPlot.X.Max = maxX
	allScatterPlot.Y.Min = 1e-5
	allScatterPlot.Y.Max = maxY
	fmt.Println("all", minX, maxX)

	b.AddSubPlot(allScatterPlot, viz.Rect{histWidth, 1 - height, scatterWidth, height})

	b.Save(16.0, 5*float64(n), filename)
}
func analyzeAuctioneerFetchStateDuration() {
	data, err := ioutil.ReadFile(config.DataDir("auctioneer-fetch-state-duration", "auctioneer-fetch-state-duration.logs"))
	say.ExitIfError("couldn't read log file", err)

	entries := util.ChugLagerEntries(data)
	fetchStateDurationEvents := ExtractEventsFromLagerData(entries, DurationExtractor("duration"))

	allDurations := fetchStateDurationEvents.Data()
	highDurations := allDurations.Filter(NumFilter(">", 0.2))
	lowDurations := allDurations.Filter(NumFilter("<=", 0.2))

	earlyAllDurations := fetchStateDurationEvents.FilterTimes(NumFilter("<", 1445274027070991039)).Data()
	earlyHighDurations := earlyAllDurations.Filter(NumFilter(">", 0.2))
	earlyLowDurations := earlyAllDurations.Filter(NumFilter("<=", 0.2))

	lateAllDurations := fetchStateDurationEvents.FilterTimes(NumFilter(">=", 1445274027070991039)).Data()
	lateHighDurations := lateAllDurations.Filter(NumFilter(">", 0.2))
	lateLowDurations := lateAllDurations.Filter(NumFilter("<=", 0.2))

	board := viz.NewUniformBoard(3, 1, 0)
	earlyScale := float64(len(allDurations)) / float64(len(earlyAllDurations))
	lateScale := float64(len(allDurations)) / float64(len(lateAllDurations))

	say.Println(0, "All Fetches:   %s", allDurations.Stats())
	say.Println(1, ">  0.2s:     %s", highDurations.Stats())
	say.Println(1, "<= 0.2s:     %s", lowDurations.Stats())
	say.Println(0, "Early Fetches: %s", earlyAllDurations.Stats())
	say.Println(1, ">  0.2s:     %s", earlyHighDurations.Stats())
	say.Println(1, "<= 0.2s:     %s", earlyLowDurations.Stats())
	say.Println(0, "Late Fetches:  %s", lateAllDurations.Stats())
	say.Println(1, ">  0.2s:     %s", lateHighDurations.Stats())
	say.Println(1, "<= 0.2s:     %s", lateLowDurations.Stats())

	p, _ := plot.New()
	p.Title.Text = "All Auctioneer Fetch State Durations"
	p.Add(viz.NewHistogram(allDurations, 20, allDurations.Min(), allDurations.Max()))
	h := viz.NewScaledHistogram(earlyAllDurations, 20, allDurations.Min(), allDurations.Max(), earlyScale)
	h.LineStyle = viz.LineStyle(viz.Blue, 1)
	p.Add(h)
	h = viz.NewScaledHistogram(lateAllDurations, 20, allDurations.Min(), allDurations.Max(), lateScale)
	h.LineStyle = viz.LineStyle(viz.Red, 1)
	p.Add(h)
	board.AddNextSubPlot(p)

	p, _ = plot.New()
	p.Title.Text = "Auctioneer Fetch State Durations &lt; 0.2s"
	p.Add(viz.NewHistogram(lowDurations, 100, lowDurations.Min(), lowDurations.Max()))
	h = viz.NewScaledHistogram(earlyLowDurations, 100, lowDurations.Min(), lowDurations.Max(), earlyScale)
	h.LineStyle = viz.LineStyle(viz.Blue, 1)
	p.Add(h)
	h = viz.NewScaledHistogram(lateLowDurations, 100, lowDurations.Min(), lowDurations.Max(), lateScale)
	h.LineStyle = viz.LineStyle(viz.Red, 1)
	p.Add(h)
	board.AddNextSubPlot(p)

	p, _ = plot.New()
	p.Title.Text = "Auctioneer Fetch State Durations > 0.2s"
	p.Add(viz.NewHistogram(highDurations, 40, highDurations.Min(), highDurations.Max()))
	h = viz.NewScaledHistogram(earlyHighDurations, 40, highDurations.Min(), highDurations.Max(), earlyScale)
	h.LineStyle = viz.LineStyle(viz.Blue, 1)
	p.Add(h)
	h = viz.NewScaledHistogram(lateHighDurations, 40, highDurations.Min(), highDurations.Max(), lateScale)
	h.LineStyle = viz.LineStyle(viz.Red, 1)
	p.Add(h)
	board.AddNextSubPlot(p)

	board.Save(12, 4, config.DataDir("auctioneer-fetch-state-duration", "auctioneer-fetch-state-duration.svg"))
}