Skip to content

Commit dcb9702

Browse files
author
Mohammed Sadique
committed
histo
1 parent e458ed0 commit dcb9702

10 files changed

Lines changed: 199 additions & 9 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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ defmodule Matplotex.Figure.Areal do
44
alias Matplotex.Figure.TwoD
55
@callback create(struct(), any(), keyword()) :: struct()
66
@callback materialize(struct()) :: struct()
7-
@callback plotify(number(), tuple(), number(), number(), list(), atom()) :: number()
87
@callback with_legend_handle(struct(), struct()) :: struct()
8+
@optional_callbacks with_legend_handle: 2
99
defmacro __using__(_) do
1010
quote do
1111
@behaviour Matplotex.Figure.Areal

lib/matplotex/figure/areal/bar_chart.ex

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

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

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/dataset.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ defmodule Matplotex.Figure.Dataset do
44
@default_linestyle "_"
55
@default_width 0.2
66
@default_marker_size 3.5
7+
@default_stroke "black"
8+
@default_alpha 1.0
79

810
defstruct [
911
:label,
@@ -13,6 +15,8 @@ defmodule Matplotex.Figure.Dataset do
1315
y: [],
1416
transformed: [],
1517
color: @default_color,
18+
edge_color: @default_stroke,
19+
alpha: @default_alpha,
1620
marker: @default_marker,
1721
linestyle: @default_linestyle,
1822
marker_size: @default_marker_size

lib/matplotex/helpers.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,13 @@ defmodule Matplotex.Helpers do
307307
|> Matplotex.show()
308308
|> copy()
309309
end
310+
311+
def hist() do
312+
values = Nx.Random.key(12) |> Nx.Random.normal(0, 1, shape: {1000}) |> elem(0) |> Nx.to_list()
313+
bins = 30
314+
315+
Matplotex.hist(values, bins, x_label: "Value", y_label: "Frequency", title: "Histogram", color: "blue", edge_color: "black", alpha: 0.7)
316+
|> Matplotex.show()
317+
|> copy()
318+
end
310319
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
defmodule Matplotex.Figure.Areal.HistogramTest do
2+
use Matplotex.PlotCase
3+
alias Matplotex.Figure.Areal.Histogram
4+
5+
setup do
6+
values = Nx.Random.key(12) |> Nx.Random.normal(0, 1, shape: {1000}) |> elem(0) |> Nx.to_list()
7+
bins = 30
8+
9+
figure =
10+
Matplotex.hist(values, bins, x_label: "Value", y_label: "Frequency", title: "Histogram")
11+
12+
{:ok, %{figure: figure, bins: bins}}
13+
end
14+
15+
describe "materialyze/1" do
16+
test "materialyze with elements", %{figure: figure, bins: bins} do
17+
assert figure = Histogram.materialize(figure)
18+
elements = figure.axes.element
19+
assert title = elements |> Enum.filter(fn elem -> elem.type == "figure.title" end) |> hd
20+
assert title.text == "Histogram"
21+
assert x_label = elements |> Enum.filter(fn elem -> elem.type == "figure.x_label" end) |> hd
22+
assert x_label.text == "Value"
23+
assert y_label = elements |> Enum.filter(fn elem -> elem.type == "figure.y_label" end) |> hd
24+
assert y_label.text == "Frequency"
25+
x_ticks = elements |> Enum.filter(fn elem -> elem.type == "figure.x_tick" end)
26+
y_ticks = elements |> Enum.filter(fn elem -> elem.type == "figure.y_tick" end)
27+
histogram_elements = elements|>Enum.filter(fn elem -> elem.type == "figure.histogram" end)
28+
assert length(histogram_elements) == bins
29+
end
30+
end
31+
end

test/matplotex_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
defmodule MatplotexTest do
2+
alias Matplotex.Figure.Dataset
23
alias Matplotex.InputError
34
use Matplotex.PlotCase
45
alias Matplotex.Figure
@@ -272,4 +273,19 @@ defmodule MatplotexTest do
272273
assert figure.rc_params.x_label_font.font_size == 10
273274
assert figure.rc_params.y_label_font.font_size == 10
274275
end
276+
277+
describe "hist" do
278+
test "creates a figure for histogram" do
279+
values =
280+
Nx.Random.key(12) |> Nx.Random.normal(0, 1, shape: {1000}) |> elem(0) |> Nx.to_list()
281+
282+
bins = 30
283+
figure = Matplotex.hist(values, bins, x_label: "Value", y_label: "Frequency")
284+
assert %Dataset{x: x, y: y} = hd(figure.axes.dataset)
285+
assert length(x) == bins
286+
assert length(y) == bins
287+
assert figure.axes.label.x == "Value"
288+
assert figure.axes.label.y == "Frequency"
289+
end
290+
end
275291
end

0 commit comments

Comments
 (0)