forked from danking/racket-ml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhistogram.rkt
143 lines (125 loc) · 5.1 KB
/
histogram.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#lang typed/racket
(require plot/typed
(only-in plot/typed/utils linear-seq)
(only-in racket/snip image-snip%)
)
(require/typed racket
[in-value (All (X) (X -> [Sequenceof X]))]
[in-cycle (All (X) ([Sequenceof X] * -> [Sequenceof X]))]
)
(provide (struct-out histogram)
histogram->function
hist-gen&render
histogram->renderer
generate-histogram
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Histogram
(struct: histogram
([bins : [Vectorof Real]]
[left : Real]
[right : Real]
[number-of-bins : Natural]
[bin-width : Real]
[which-bin : (Real -> Natural)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(: histogram->function : (histogram -> (Real -> Real)))
(define/match (histogram->function h)
[((histogram bins left right _ _ which-bin))
(lambda: ([n : Real])
(when (or (< n left) (> n right))
(error 'histogram-as-function
"The value ~a is not within the range of this histogram [~a,~a]"
n left right))
((inst vector-ref Real) bins (which-bin n)))])
(: bucketize : ((Sequenceof Real)
(Sequenceof Real)
Natural
(Real -> Natural)
Real
[#:normalize? Boolean]
->
(Vectorof Real)))
(define (bucketize data
weights
number-of-bins
which-bin
bin-width
#:normalize? [normalize? #f])
(define bins
(for/fold:
([hist : [Vectorof Real] (make-vector number-of-bins 0)])
([d : Real data]
[n : Real weights])
(let ((bin (which-bin d)))
(vector-set! hist bin (+ (vector-ref hist bin) n))
hist)))
(if normalize? (normalize-vector bins bin-width) bins))
(: generate-which-bin : (Real Real Natural Real -> (Real -> Natural)))
;; generate a function which partitions the line segment [left, right] into
;; number-of-bins (almost) equally sized bins.
;;
;; NB: The last bin is inclusive of both endpoints whereas all other bins are
;; exclusive of the right-most endpoint.
(define (generate-which-bin left right number-of-bins bin-width)
(lambda: ([value : Real])
(if (and (>= value left) (<= value right))
(max (min (exact-floor (/ (- value left) bin-width))
(sub1 number-of-bins)) ; catch the right end-point
0) ; appease the type checker
(error 'histogram-which-bin
"Value ~a is not within range [~a,~a]."
value left right))))
(: normalize-vector ((Vectorof Real) Real -> (Vectorof Real)))
(define (normalize-vector v bin-width)
(define sum (for/sum: : Real ([value : Real (in-vector v)])
(* bin-width value)))
(for/vector: : (Vectorof Real) ([value : Real (in-vector v)])
(/ value sum)))
(: sequence-of-ones : [Sequenceof Natural])
(define sequence-of-ones (in-cycle (in-value 1)))
(: generate-histogram : ([Sequenceof Real]
Natural
[#:weights [Sequenceof Natural]]
[#:normalize? Boolean]
->
histogram))
(define (generate-histogram data
number-of-bins
#:weights [weights sequence-of-ones]
#:normalize? [normalize? #t])
(define left (exact-floor (apply min (sequence->list data))))
(define right (exact-ceiling (apply max (sequence->list data))))
(define total-width (max 0 (- right left)))
(define bin-width (max 0 (/ total-width number-of-bins)))
(define which-bin
(generate-which-bin left right number-of-bins bin-width))
(histogram (bucketize data
weights
number-of-bins
which-bin
bin-width
#:normalize? normalize?)
left right
number-of-bins
bin-width
which-bin))
(: histogram->renderer : (histogram -> renderer2d))
(define/match (histogram->renderer h)
[((histogram _ left right number-of-bins _ _))
(area-histogram (histogram->function h) (linear-seq left right number-of-bins))])
;; poorly named, but skips the intermediate step for the impatient among us
(: hist-gen&render : ([Sequenceof Real]
Natural
[#:weights [Sequenceof Natural]]
[#:normalize? Boolean]
->
renderer2d))
(define (hist-gen&render data
number-of-bins
#:weights [weights sequence-of-ones]
#:normalize? [normalize? #t])
(histogram->renderer (generate-histogram data
number-of-bins
#:weights weights
#:normalize? normalize?)))