Skip to content

Commit 46cf1d2

Browse files
Merge pull request #24 from BigThinkcode/histogram
Histogram
2 parents e458ed0 + dd2253f commit 46cf1d2

16 files changed

Lines changed: 219 additions & 247 deletions

File tree

lib/matplotex.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule Matplotex do
22
@moduledoc """
33
Module to generate a graph.
44
"""
5+
alias Matplotex.Figure.Areal.Histogram
56
alias Matplotex.InputError
67
alias Matplotex.Figure.Radial.Pie
78
alias Matplotex.Figure.Areal.Scatter
@@ -83,6 +84,10 @@ defmodule Matplotex do
8384
|> LinePlot.create({x, y}, opts)
8485
end
8586

87+
def hist(data, bins, opts) do
88+
Histogram.create(%Figure{axes: %Histogram{}}, {data, bins}, opts)
89+
end
90+
8691
@doc """
8792
Sets X and Y labels for the graph with given font details
8893

lib/matplotex/element/rect.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
defmodule Matplotex.Element.Rect do
22
alias Matplotex.Element
33

4-
@default_stroke_width 1
5-
@default_stroke "rgba(0,0,0,0)"
4+
@default_stroke_width 2
5+
@default_stroke "black"
6+
@default_opacity 1.0
67
use Element
78

89
@type t :: %__MODULE__{
@@ -22,6 +23,8 @@ defmodule Matplotex.Element.Rect do
2223
:color,
2324
:type,
2425
stroke: @default_stroke,
26+
fill_opacity: @default_opacity,
27+
stroke_opacity: @default_opacity,
2528
stroke_width: @default_stroke_width
2629
]
2730

@@ -37,6 +40,8 @@ defmodule Matplotex.Element.Rect do
3740
width="#{get_width(rect)}"
3841
height="#{get_height(rect)}"
3942
stroke-width="#{rect.stroke_width}"
43+
stroke-opacity="#{rect.stroke_opacity}"
44+
fill-opacity="#{rect.fill_opacity}"
4045
filter="">
4146
</rect>)
4247
end

lib/matplotex/figure/areal.ex

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
defmodule Matplotex.Figure.Areal do
2+
alias Matplotex.Figure.Areal.Ticker
23
alias Matplotex.Utils.Algebra
34
alias Matplotex.Figure.Dataset
45
alias Matplotex.Figure.TwoD
56
@callback create(struct(), any(), keyword()) :: struct()
67
@callback materialize(struct()) :: struct()
7-
@callback plotify(number(), tuple(), number(), number(), list(), atom()) :: number()
88
@callback with_legend_handle(struct(), struct()) :: struct()
9+
@optional_callbacks with_legend_handle: 2
910
defmacro __using__(_) do
1011
quote do
1112
@behaviour Matplotex.Figure.Areal
@@ -79,8 +80,13 @@ defmodule Matplotex.Figure.Areal do
7980
|> update_tick(tick)
8081
end
8182

82-
def add_ticks(%__MODULE__{tick: tick, size: size} = axes, {key, {_min, _max} = lim}) do
83-
{ticks, lim} = __MODULE__.generate_ticks(lim)
83+
def add_ticks(%__MODULE__{tick: tick, size: {width, height}= size} = axes, {key, {_min, _max} = lim}) do
84+
number_of_ticks = if key == :x do
85+
ceil(width)
86+
else
87+
ceil(height)
88+
end
89+
{ticks, lim} = Ticker.generate_ticks(lim, number_of_ticks )
8490

8591
tick = Map.put(tick, key, ticks)
8692

@@ -116,23 +122,6 @@ defmodule Matplotex.Figure.Areal do
116122
%{axes | legend: legend}
117123
end
118124

119-
def generate_xticks(%module{data: {x, _y}, tick: tick, limit: limit} = axes) do
120-
{xticks, xlim} =
121-
module.generate_ticks(x)
122-
123-
tick = Map.put(tick, :x, xticks)
124-
limit = update_limit(limit, :x, xlim)
125-
%__MODULE__{axes | tick: tick, limit: limit}
126-
end
127-
128-
def generate_yticks(%module{data: {_x, y}, tick: tick, limit: limit} = axes) do
129-
{yticks, ylim} =
130-
module.generate_ticks(y)
131-
132-
tick = Map.put(tick, :y, yticks)
133-
limit = update_limit(limit, :y, ylim)
134-
%__MODULE__{axes | tick: tick, limit: limit}
135-
end
136125

137126
defp update_limit(%TwoD{x: nil} = limit, :x, xlim) do
138127
%TwoD{limit | x: xlim}

lib/matplotex/figure/areal/bar_chart.ex

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,6 @@ defmodule Matplotex.Figure.Areal.BarChart do
9191
%Figure{figure | axes: %{axes | element: elements_with_bar}}
9292
end
9393

94-
@impl Areal
95-
def plotify(value, {minl, maxl}, axis_size, transition, _data, :x) do
96-
s = axis_size / (maxl - minl)
97-
value * s + transition - minl * s
98-
end
99-
100-
def plotify(value, {minl, maxl}, axis_size, transition, _data, :y) do
101-
s = axis_size / (maxl - minl)
102-
value * s + transition - minl * s
103-
end
104-
10594
@impl Areal
10695
def with_legend_handle(
10796
%Legend{x: x, y: y, color: color, width: width, height: height} = legend,
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
defmodule Matplotex.Figure.Areal.Histogram do
2+
alias Matplotex.Element.Rect
3+
alias Matplotex.Figure.RcParams
4+
alias Matplotex.Figure.Areal.PlotOptions
5+
alias Matplotex.Figure.Dataset
6+
alias Matplotex.Figure.Areal.Region
7+
alias Matplotex.Figure
8+
alias Matplotex.Figure.Areal
9+
use Areal
10+
11+
@make_it_zero 0
12+
13+
frame(
14+
tick: %TwoD{},
15+
limit: %TwoD{},
16+
label: %TwoD{},
17+
scale: %TwoD{},
18+
region_x: %Region{},
19+
region_y: %Region{},
20+
region_title: %Region{},
21+
region_legend: %Region{},
22+
region_content: %Region{}
23+
)
24+
25+
@impl Areal
26+
def create(%Figure{axes: %__MODULE__{} = axes, rc_params: rc_params} = figure, {data, bins}, opts) do
27+
{x, y} = bins_and_hists(data, bins)
28+
29+
dataset = Dataset.cast(%Dataset{x: x, y: y}, opts)
30+
%Figure{figure | axes: %__MODULE__{axes | data: {x, y}, dataset: [dataset]}, rc_params: %RcParams{rc_params | y_padding: @make_it_zero}}
31+
|> PlotOptions.set_options_in_figure(opts)
32+
end
33+
34+
@impl Areal
35+
def materialize(figure) do
36+
figure
37+
|> sanitize()
38+
|> __MODULE__.materialized_by_region()
39+
|> materialize_hist()
40+
end
41+
42+
defp materialize_hist(%Figure{axes: %{dataset: data,limit: %TwoD{x: x_lim, y: y_lim}, region_content: %Region{x: x_region_content, y: y_region_content, width: width_region_content, height: height_region_content}, element: element}, rc_params: %RcParams{x_padding: x_padding, white_space: white_space}}) do
43+
x_padding_value = width_region_content * x_padding + white_space
44+
shrinked_width_region_content = width_region_content - x_padding_value * 2
45+
46+
hist_elements =
47+
data
48+
|>Enum.map(fn dataset ->
49+
dataset
50+
|> do_transform(x_lim, y_lim, shrinked_width_region_content, height_region_content, {x_region_content + x_padding_value, y_region_content})
51+
|> capture(abs(y_region_content), shrinked_width_region_content)
52+
end)
53+
|>List.flatten()
54+
%Figure{axes: %{element: element ++ hist_elements}}
55+
end
56+
57+
defp capture(%Dataset{transformed: transformed} = dataset, bly, region_width) do
58+
capture(transformed, [], dataset, bly, region_width)
59+
end
60+
61+
defp capture(
62+
[{x, y} | to_capture],
63+
captured,
64+
%Dataset{
65+
color: color,
66+
x: bins,
67+
pos: pos_factor,
68+
edge_color: edge_color,
69+
alpha: alpha
70+
} = dataset,
71+
bly,
72+
region_width
73+
) do
74+
capture(
75+
to_capture,
76+
captured ++
77+
[
78+
%Rect{
79+
type: "figure.histogram",
80+
x: bin_position(x, pos_factor),
81+
y: y,
82+
width: region_width / length(bins),
83+
height: bly - y,
84+
color: color,
85+
stroke: edge_color,
86+
fill_opacity: alpha,
87+
stroke_opacity: alpha
88+
}
89+
],
90+
dataset,
91+
bly,
92+
region_width
93+
)
94+
end
95+
96+
defp capture([], captured, _dataset, _bly, _region_width), do: captured
97+
98+
99+
defp bin_position(x, pos_factor) when pos_factor < 0 do
100+
x + pos_factor
101+
end
102+
103+
defp bin_position(x, _pos_factor), do: x
104+
defp bins_and_hists(data, bins) do
105+
{data_min, data_max} = Enum.min_max(data)
106+
107+
bins_dist =
108+
data_min
109+
|> Nx.linspace(data_max, n: bins)
110+
|> Nx.to_list()
111+
112+
{hists, _} =
113+
Enum.map_reduce(bins_dist, data_min, fn bin, previous_bin ->
114+
frequency = Enum.frequencies_by(data, fn point -> point < bin && point > previous_bin end)
115+
{Map.get(frequency, true, 0), bin}
116+
end)
117+
118+
{bins_dist, hists}
119+
end
120+
121+
defp sanitize(%Figure{axes: %__MODULE__{data: {x, y}}= axes} = figure) do
122+
{ymin, ymax} = Enum.min_max(y)
123+
{xmin, xmax} = Enum.min_max(x)
124+
125+
%Figure{figure | axes: %__MODULE__{axes | limit: %TwoD{x: {floor(xmin), ceil(xmax)},y: {floor(ymin), ceil(ymax)}}}}
126+
end
127+
end

lib/matplotex/figure/areal/line_plot.ex

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,6 @@ defmodule Matplotex.Figure.Areal.LinePlot do
9393
%Figure{figure | axes: %{axes | element: elements}}
9494
end
9595

96-
@impl Areal
97-
def plotify(value, {minl, maxl}, axis_size, transition, _, _) do
98-
s = axis_size / (maxl - minl)
99-
value * s + transition - minl * s
100-
end
101-
10296
@impl Areal
10397
def with_legend_handle(
10498
%Legend{x: x, y: y, color: color, width: marker_size} = legend,

lib/matplotex/figure/areal/plot_options.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,9 @@ defmodule Matplotex.Figure.Areal.PlotOptions do
117117
%Figure{
118118
figure
119119
| axes: axes |> struct(opts) |> cast_two_d_structs(opts)
120-
# |>fulfill_tick_and_lim()
121120
}
122121
end
123122

124-
# defp fulfill_tick_and_lim(%{tick: nil, limit: nil} = axes) do
125-
126-
# end
127-
128123
defp cast_two_d_structs(%{label: label, tick: tick, limit: limit} = axes, opts)
129124
when is_map(opts) do
130125
%{

lib/matplotex/figure/areal/scatter.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ defmodule Matplotex.Figure.Areal.Scatter do
132132
end
133133

134134
defp capture(_, captured, _), do: captured
135-
@impl Areal
136-
def plotify(value, {minl, maxl}, axis_size, transition, _, _) do
137-
s = axis_size / (maxl - minl)
138-
value * s + transition - minl * s
139-
end
140135

141136
@impl Areal
142137
def with_legend_handle(

lib/matplotex/figure/areal/ticker.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ defmodule Matplotex.Figure.Areal.Ticker do
44
step = (max - min) / @tick_in_plot
55
produce_ticks(min, max, step, [format_number(min)])
66
end
7+
def generate_ticks({lower_limit, upper_limit} = lim, number_of_ticks) do
8+
{lower_limit |> Nx.linspace(upper_limit, n: number_of_ticks) |> Nx.to_list(), lim}
9+
end
710

811
defp produce_ticks(value, max, _step, ticks) when value >= max do
912
Enum.reverse(ticks)

0 commit comments

Comments
 (0)