From fc558766be1906f6f9585f97fa7273a5e34b0e55 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Tue, 7 Jan 2025 19:21:40 +0530 Subject: [PATCH 1/5] module doc --- lib/matplotex.ex | 112 ++++++++++++++++++++++++++++++++++- lib/matplotex/figure/font.ex | 2 +- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index 4ea2146..be2d862 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -1,6 +1,116 @@ defmodule Matplotex do @moduledoc """ - Module to generate a graph. + # Matplotex + + a lightweight and efficient library for Elixir projects that facilitates server-side + SVG generation for data visualization. Designed to integrate seamlessly with Phoenix LiveView, + it serves as a powerful tool for creating dynamic visualizations in web applications. + + it supports the following graphs + - Line plots + - Bar charts + - Pie charts + - Spline graphs + - Histograms + - Scatter plots + + The plotting of a graph comes with set of common parameters and set of plot specific parameters + all of them will share with the corresponding function documentation, this section covers one examaple + as a line plot. + There are two approach to generate plots + - by using specific function to set parameters + - by using parameters along with options + + ```elixir + alias Matplotex as: M + + x = [1, 2, 3, 4, 6, 6, 7] + y = [1, 3, 4, 4, 5, 6, 7] + + frame_width = 6 + frame_height = 6 + size = {frame_width, frame_height} + margin = 0.05 + font_size = "16pt" + title_font_size = "18pt" + ticks = [1, 2, 3, 4, 5, 6, 7] + + x + |> M.plot(y) + |> M.figure(%{figsize: size, margin: margin}) + |> M.set_title("The Plot Title") + |> M.set_xlabel("X Axis") + |> M.set_ylabel("Y Axis") + |> M.set_xticks(ticks) + |> M.set_yticks(ticks) + |> M.set_xlim({4, 7}) + |> M.set_ylim({4, 7}) + |> M.set_rc_params( + x_tick_font_size: font_size, + y_tick_font_size: font_size, + title_font_size: title_font_size, + x_label_font_size: font_size, + y_label_font_size: font_size, + title_font_size: title_font_size + ) + |> M.show() + ``` + This module exposes all of the functions for setters + and another approach is creating plots by using plot options the code is as follows + ```elixir + alis Matplotex as: M + x = [1, 2, 3, 4, 6, 6, 7] + y = [1, 3, 4, 4, 5, 6, 7] + + frame_width = 6 + frame_height = 6 + size = {frame_width, frame_height} + margin = 0.05 + font_size = "16pt" + title_font_size = "18pt" + ticks = [0, 1, 2, 3, 4, 5, 6, 7] + + x + |> M.plot(y, + figsize: size, + margin: margin, + title: "The plot title", + x_label: "X Axis", + y_label: "Y Axis", + x_tick: ticks, + y_tick: ticks, + x_limit: {0, 7}, + y_limit: {0, 7}, + x_tick_font_size: font_size, + y_tick_font_size: font_size, + title_font_size: title_font_size, + x_label_font_size: font_size, + y_label_font_size: font_size, + y_tick_font_text_anchor: "start" + ) + |> M.show() + ``` + just for simplicity and convenience of the user it is keeping both patterns, no difference on using one on another + + So the user has the control on the all parameters on the inner elements of the chart + + ## Rc Params + In the first example along with the setter functions you might noticed M.set_rc_params/2 + The role of this function is similar to other functions we are keeping some values with the plot data + and the acronym RC stands for Runtime configuration, the plot data holds the labels limits ticks, etc + The RC params are holding the font size, color, style etc, by defaul one chart object kickstart with some default values + just for the sake of it needed some values, by default all the fonts are Areal, Veradana, sans-serif and using standard font size 12 + if a user creates a plots with no inputs for any of these the plot will be choosing the default values + besides font configuration Rc params covers + `line_width, line_style, grid_color, grid_linestyle, grid_alpha, tick_line_length, x_padding, y_padding, legend_width` + There is two types of padding `x_padding, y_padding` and `padding` the perfect use of those can be found on upcoming plot specific documentations + + ## Elements + The output format of the plot is SVG will support more formats in future, anyway the svg is a group of some elements put together, throught the execution + it is generating those elements through elixir data structure, all element data structure contains some svg equivalent data that converts the elements to + SVG string, the output SVG string can be used directly in the web application. + + """ alias Matplotex.Figure.Areal.Spline alias Matplotex.Figure.Areal.Histogram diff --git a/lib/matplotex/figure/font.ex b/lib/matplotex/figure/font.ex index e414e73..f484c8f 100644 --- a/lib/matplotex/figure/font.ex +++ b/lib/matplotex/figure/font.ex @@ -2,7 +2,7 @@ defmodule Matplotex.Figure.Font do @default_font_color "black" @default_font_family "Arial, Verdana, sans-serif" @default_font_style "normal" - @default_font_size 16 + @default_font_size 12 @default_font_weight "normal" @font_unit "pt" @pt_to_inch_ratio 1 / 72 From 0850fbb22e7c27d0a024791cebc4851c5b3f9058 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Wed, 8 Jan 2025 18:47:58 +0530 Subject: [PATCH 2/5] function based documentation --- lib/matplotex.ex | 187 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 176 insertions(+), 11 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index be2d862..cdeeeaa 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -110,6 +110,17 @@ defmodule Matplotex do it is generating those elements through elixir data structure, all element data structure contains some svg equivalent data that converts the elements to SVG string, the output SVG string can be used directly in the web application. + ## Figure + The execution a plot carriec out by a data structure named Matplotex.Figure it holds all the adequate information to generate a figure it containst the keys + `:figsize` - is a tuple carries width and height of the figure eg: {10,6} + `:axes` - is another object that will varie according to the plot + `:rc_params` - the runtime configurations + `:margin` - the margin of the figure + + ## M.show/1 + All examples above using this function `M.show/1` after a plot generation API call + The all APIs from this module is ment to return an svg equivalent Data Matplotex.Figure with distinct object associated with the `axes` key, so to convert that data to + an SVG chart use `M.show/1` """ alias Matplotex.Figure.Areal.Spline @@ -121,7 +132,36 @@ defmodule Matplotex do alias Matplotex.Figure.Sketch alias Matplotex.Figure alias Matplotex.Figure.Areal.BarChart + @doc """ + Generates a bar chart using the provided values and bar widths. + +## Parameters + + - `values` (list of numbers): A list of numerical values representing the heights of the bars in the chart. + - `width` (floatiung point number): The width of each bar in inches. + - `opts` (keyword list): It will support all opts mentioned above, some bar specific options are there those are + - `:label` (string): Label for specific dataset passed on first argument. + - `:color` (string): Color of the bar. + - `:edge_color` (string): Color of the edge of the bar. + + +## Returns + + - A figure with the axes of a bar chart +```elixir + alias Matplotex as: M + categories = ["apple", "banana", "fig", "avocado"] + values1 = [22, 33, 28, 34] + iex> Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") + %M.Figure{axes: %M.Figure.Areal.BarChart{...}, ...} + ``` + +This function takes a list of numerical `values` and a single `width` value to create a bar chart where: + - The height of each bar corresponds to its respective value from the list. + - Each bar has the specified constant width. +""" + @spec bar(list(), float()) :: Matplotex.Figure.t() def bar(values, width) do bar(width, values, width, []) end @@ -137,7 +177,33 @@ defmodule Matplotex do def bar(pos, values, width, opts) do BarChart.create(%Figure{axes: %BarChart{}}, {pos, values, width}, opts) end + @doc """ +Adds an additional dataset to a bar plot in the given `%Figure{}`. + +This function allows you to append multiple datasets to a bar plot by providing new values and corresponding options. Each dataset can be customized with options such as color, label, and bar width. + +## Parameters + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `values` (list): A list of numerical values representing the heights of the bars in the new dataset. + - `width` (float): The width of the bars in the dataset. + - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color and label. + +## Usage + +This function is used when generating multi-bar plots to represent data from multiple datasets. Here's an example demonstrating its usage: + +```elixir +alias Matplotex, as: M + +categories = ["apple", "banana", "fig", "avocado"] +values1 = [22, 33, 28, 34] +values2 = [53, 63, 59, 60] +width = 0.22 + +Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") +|> M.bar(width, values2, width, label: "Dataset2", color: "#D3D3D3") +""" def bar(%Figure{} = figure, pos, values, width, opts) do figure |> show_legend() @@ -150,7 +216,26 @@ defmodule Matplotex do def scatter(stream, opts) when is_struct(stream, Stream) do Scatter.create(stream, opts) end + @doc """ +Creates a scatter plot based on the given `x` and `y` values, with optional customization provided via `opts`. +## Parameters + + - `x` (list): A list of numerical values representing the x-coordinates. + - `y` (list): A list of numerical values representing the y-coordinates. + - `opts` (keyword list, optional): A set of options for customizing the scatter plot, such as color, marker size, and labels. + +## Examples + +```elixir +# Basic usage: +x = [1, 2, 3, 4] +y = [10, 20, 30, 40] +opts = [color: "blue", marker_size: 5] + +iex> M.scatter(x, y, opts) + %M.Figure{axes: %Matplotex.Figure.Areal.Scatter{...}, ...} +""" def scatter(x, y) do scatter(x, y, []) end @@ -158,7 +243,39 @@ defmodule Matplotex do def scatter(x, y, opts) do Scatter.create(%Figure{axes: %Scatter{}}, {x, y}, opts) end + @doc """ +Adds an additional dataset to a scatter plot in the given `%Figure{}`. + +This function allows you to overlay multiple scatter plots on the same figure by providing new `x` and `y` values, along with customization options via `opts`. +## Parameters + + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `x` (list): A list of numerical values representing the x-coordinates of the new dataset. + - `y` (list): A list of numerical values representing the y-coordinates of the new dataset. + - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color, marker style, line style, and labels. + +## Usage + +This function is typically used when you want to generate multi-pattern scatter plots with multiple datasets. The following example demonstrates its usage: + +```elixir +x = [1, 2, 3, 4, 5] + +# Dataset 1 +y1 = [20, 5, 12, 16, 25] + +# Dataset 2 +y2 = [10, 1, 6, 10, 15] + +# Dataset 3 +y3 = [17, 5, 8, 12, 17] + +x +|> Matplotex.scatter(y1, color: "blue", linestyle: "_", marker: "o", label: "Dataset 1") +|> Matplotex.scatter(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") +|> Matplotex.scatter(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") +""" def scatter(%Figure{} = figure, x, y, opts) do figure |> show_legend() @@ -166,22 +283,70 @@ defmodule Matplotex do end @doc """ - Creates a piec charts based on the size and opts - """ - def pie(sizes, opts \\ []) do - Pie.create(%Figure{axes: %Pie{}}, sizes, opts) - end +Generates a pie chart based on the provided data, labels, and options. - @doc """ - Creates a line plot with given x and y data. +### Parameters: +- `sizes` (list of integers/floats): Percentages or proportions for each slice of the pie chart. +- `opts` (keyword list): Options to customize the chart, such as: + - `:labels` (list of strings): Labels for each slice. + - `:colors` (list of strings): Colors for the slices. - ## Examples +### Example: - iex> Matplotex.plot([1, 2, 3], [4, 5, 6]) - %Matplotex.Figure{} +```elixir +# Percentages for each slice +sizes = [25, 35, 20, 20] - """ +# Labels for each slice +labels = ["A", "B", "C", "D"] + +# Colors for the slices +colors = ["lightblue", "lightgreen", "orange", "pink"] + +# Generate the pie chart +sizes +|> Matplotex.pie(colors: colors, labels: labels) + +%M.Figure{axes: %Matplotex.Figure.Radial.Pie{...}, ...} +""" + def pie(sizes, opts \\ []) do + Pie.create(%Figure{axes: %Pie{}}, sizes, opts) + end + @doc """ +Generates a line plot using the provided `x` and `y` data points. +##Parameters +`x`: A list of x-coordinates for the data points (e.g., [1, 2, 3]). +`y`: A list of y-coordinates corresponding to x (e.g., [2, 4, 6]). +`opts`: it also expect some plot specific options such as + `color`: line color + `linestyle`: line style + `marker`: marker style + +### Example + +```elixir +# Define the data points +x = [1, 2, 3, 4, 6, 6, 7] +y = [1, 3, 4, 4, 5, 6, 7] + +# Specify plot configurations +frame_width = 6 +frame_height = 6 +size = {frame_width, frame_height} +margin = 0.05 +font_size = "16pt" +title_font_size = "18pt" +ticks = [1, 2, 3, 4, 5, 6, 7] + +# Create and configure the plot +x +|> Matplotex.plot(y) # Create a line plot +|> Matplotex.figure(%{ # Configure the figure + figsize: size, + margin: margin + }) +""" @spec plot(list(), list()) :: Figure.t() def plot(x, y) when is_list(x) and is_list(y) do plot(x, y, []) From 92bf411718b64c79fa66fba6c3d45ca2397d1758 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 9 Jan 2025 15:29:50 +0530 Subject: [PATCH 3/5] remining functional documentations --- lib/matplotex.ex | 437 ++++++++++++++------ lib/matplotex/element/polygon.ex | 1 - lib/matplotex/figure/areal/histogram.ex | 7 +- lib/matplotex/figure/areal/spline.ex | 71 ++-- lib/matplotex/figure/two_d.ex | 6 +- lib/matplotex/helpers.ex | 33 +- test/matplotex/figure/areal/spline_test.exs | 22 +- 7 files changed, 400 insertions(+), 177 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index cdeeeaa..37276f8 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -132,35 +132,36 @@ defmodule Matplotex do alias Matplotex.Figure.Sketch alias Matplotex.Figure alias Matplotex.Figure.Areal.BarChart + @doc """ - Generates a bar chart using the provided values and bar widths. + Generates a bar chart using the provided values and bar widths. -## Parameters + ## Parameters - - `values` (list of numbers): A list of numerical values representing the heights of the bars in the chart. - - `width` (floatiung point number): The width of each bar in inches. - - `opts` (keyword list): It will support all opts mentioned above, some bar specific options are there those are - - `:label` (string): Label for specific dataset passed on first argument. - - `:color` (string): Color of the bar. - - `:edge_color` (string): Color of the edge of the bar. + - `values` (list of numbers): A list of numerical values representing the heights of the bars in the chart. + - `width` (floatiung point number): The width of each bar in inches. + - `opts` (keyword list): It will support all opts mentioned above, some bar specific options are there those are + - `:label` (string): Label for specific dataset passed on first argument. + - `:color` (string): Color of the bar. + - `:edge_color` (string): Color of the edge of the bar. -## Returns + ## Returns - - A figure with the axes of a bar chart -```elixir + - A figure with the axes of a bar chart + ```elixir - alias Matplotex as: M - categories = ["apple", "banana", "fig", "avocado"] - values1 = [22, 33, 28, 34] - iex> Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") - %M.Figure{axes: %M.Figure.Areal.BarChart{...}, ...} - ``` + alias Matplotex as: M + categories = ["apple", "banana", "fig", "avocado"] + values1 = [22, 33, 28, 34] + iex> Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") + %M.Figure{axes: %M.Figure.Areal.BarChart{...}, ...} + ``` -This function takes a list of numerical `values` and a single `width` value to create a bar chart where: - - The height of each bar corresponds to its respective value from the list. - - Each bar has the specified constant width. -""" + This function takes a list of numerical `values` and a single `width` value to create a bar chart where: + - The height of each bar corresponds to its respective value from the list. + - Each bar has the specified constant width. + """ @spec bar(list(), float()) :: Matplotex.Figure.t() def bar(values, width) do bar(width, values, width, []) @@ -177,33 +178,34 @@ This function takes a list of numerical `values` and a single `width` value to c def bar(pos, values, width, opts) do BarChart.create(%Figure{axes: %BarChart{}}, {pos, values, width}, opts) end + @doc """ -Adds an additional dataset to a bar plot in the given `%Figure{}`. + Adds an additional dataset to a bar plot in the given `%Figure{}`. -This function allows you to append multiple datasets to a bar plot by providing new values and corresponding options. Each dataset can be customized with options such as color, label, and bar width. + This function allows you to append multiple datasets to a bar plot by providing new values and corresponding options. Each dataset can be customized with options such as color, label, and bar width. -## Parameters + ## Parameters - - `figure` (%Figure{}): The figure to which the new dataset will be added. - - `values` (list): A list of numerical values representing the heights of the bars in the new dataset. - - `width` (float): The width of the bars in the dataset. - - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color and label. + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `values` (list): A list of numerical values representing the heights of the bars in the new dataset. + - `width` (float): The width of the bars in the dataset. + - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color and label. -## Usage + ## Usage -This function is used when generating multi-bar plots to represent data from multiple datasets. Here's an example demonstrating its usage: + This function is used when generating multi-bar plots to represent data from multiple datasets. Here's an example demonstrating its usage: -```elixir -alias Matplotex, as: M + ```elixir + alias Matplotex, as: M -categories = ["apple", "banana", "fig", "avocado"] -values1 = [22, 33, 28, 34] -values2 = [53, 63, 59, 60] -width = 0.22 + categories = ["apple", "banana", "fig", "avocado"] + values1 = [22, 33, 28, 34] + values2 = [53, 63, 59, 60] + width = 0.22 -Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") -|> M.bar(width, values2, width, label: "Dataset2", color: "#D3D3D3") -""" + Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") + |> M.bar(width, values2, width, label: "Dataset2", color: "#D3D3D3") + """ def bar(%Figure{} = figure, pos, values, width, opts) do figure |> show_legend() @@ -216,26 +218,27 @@ Matplotex.bar(width, values1, width, label: "Dataset1", color: "#255199") def scatter(stream, opts) when is_struct(stream, Stream) do Scatter.create(stream, opts) end + @doc """ -Creates a scatter plot based on the given `x` and `y` values, with optional customization provided via `opts`. + Creates a scatter plot based on the given `x` and `y` values, with optional customization provided via `opts`. -## Parameters + ## Parameters - - `x` (list): A list of numerical values representing the x-coordinates. - - `y` (list): A list of numerical values representing the y-coordinates. - - `opts` (keyword list, optional): A set of options for customizing the scatter plot, such as color, marker size, and labels. + - `x` (list): A list of numerical values representing the x-coordinates. + - `y` (list): A list of numerical values representing the y-coordinates. + - `opts` (keyword list, optional): A set of options for customizing the scatter plot, such as color, marker size, and labels. -## Examples + ## Examples -```elixir -# Basic usage: -x = [1, 2, 3, 4] -y = [10, 20, 30, 40] -opts = [color: "blue", marker_size: 5] + ```elixir + # Basic usage: + x = [1, 2, 3, 4] + y = [10, 20, 30, 40] + opts = [color: "blue", marker_size: 5] -iex> M.scatter(x, y, opts) - %M.Figure{axes: %Matplotex.Figure.Areal.Scatter{...}, ...} -""" + iex> M.scatter(x, y, opts) + %M.Figure{axes: %Matplotex.Figure.Areal.Scatter{...}, ...} + """ def scatter(x, y) do scatter(x, y, []) end @@ -243,39 +246,40 @@ iex> M.scatter(x, y, opts) def scatter(x, y, opts) do Scatter.create(%Figure{axes: %Scatter{}}, {x, y}, opts) end - @doc """ -Adds an additional dataset to a scatter plot in the given `%Figure{}`. -This function allows you to overlay multiple scatter plots on the same figure by providing new `x` and `y` values, along with customization options via `opts`. + @doc """ + Adds an additional dataset to a scatter plot in the given `%Figure{}`. -## Parameters + This function allows you to overlay multiple scatter plots on the same figure by providing new `x` and `y` values, along with customization options via `opts`. - - `figure` (%Figure{}): The figure to which the new dataset will be added. - - `x` (list): A list of numerical values representing the x-coordinates of the new dataset. - - `y` (list): A list of numerical values representing the y-coordinates of the new dataset. - - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color, marker style, line style, and labels. + ## Parameters + + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `x` (list): A list of numerical values representing the x-coordinates of the new dataset. + - `y` (list): A list of numerical values representing the y-coordinates of the new dataset. + - `opts` (keyword list, optional): A set of options for customizing the appearance of the new dataset, such as color, marker style, line style, and labels. -## Usage + ## Usage -This function is typically used when you want to generate multi-pattern scatter plots with multiple datasets. The following example demonstrates its usage: + This function is typically used when you want to generate multi-pattern scatter plots with multiple datasets. The following example demonstrates its usage: -```elixir -x = [1, 2, 3, 4, 5] + ```elixir + x = [1, 2, 3, 4, 5] -# Dataset 1 -y1 = [20, 5, 12, 16, 25] + # Dataset 1 + y1 = [20, 5, 12, 16, 25] -# Dataset 2 -y2 = [10, 1, 6, 10, 15] + # Dataset 2 + y2 = [10, 1, 6, 10, 15] -# Dataset 3 -y3 = [17, 5, 8, 12, 17] + # Dataset 3 + y3 = [17, 5, 8, 12, 17] -x -|> Matplotex.scatter(y1, color: "blue", linestyle: "_", marker: "o", label: "Dataset 1") -|> Matplotex.scatter(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") -|> Matplotex.scatter(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") -""" + x + |> Matplotex.scatter(y1, color: "blue", linestyle: "_", marker: "o", label: "Dataset 1") + |> Matplotex.scatter(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") + |> Matplotex.scatter(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") + """ def scatter(%Figure{} = figure, x, y, opts) do figure |> show_legend() @@ -283,70 +287,70 @@ x end @doc """ -Generates a pie chart based on the provided data, labels, and options. + Generates a pie chart based on the provided data, labels, and options. -### Parameters: -- `sizes` (list of integers/floats): Percentages or proportions for each slice of the pie chart. -- `opts` (keyword list): Options to customize the chart, such as: - - `:labels` (list of strings): Labels for each slice. - - `:colors` (list of strings): Colors for the slices. + ### Parameters: + - `sizes` (list of integers/floats): Percentages or proportions for each slice of the pie chart. + - `opts` (keyword list): Options to customize the chart, such as: + - `:labels` (list of strings): Labels for each slice. + - `:colors` (list of strings): Colors for the slices. -### Example: + ### Example: -```elixir -# Percentages for each slice -sizes = [25, 35, 20, 20] + ```elixir + # Percentages for each slice + sizes = [25, 35, 20, 20] -# Labels for each slice -labels = ["A", "B", "C", "D"] + # Labels for each slice + labels = ["A", "B", "C", "D"] -# Colors for the slices -colors = ["lightblue", "lightgreen", "orange", "pink"] + # Colors for the slices + colors = ["lightblue", "lightgreen", "orange", "pink"] -# Generate the pie chart -sizes -|> Matplotex.pie(colors: colors, labels: labels) + # Generate the pie chart + sizes + |> Matplotex.pie(colors: colors, labels: labels) -%M.Figure{axes: %Matplotex.Figure.Radial.Pie{...}, ...} -""" + %M.Figure{axes: %Matplotex.Figure.Radial.Pie{...}, ...} + """ def pie(sizes, opts \\ []) do Pie.create(%Figure{axes: %Pie{}}, sizes, opts) end - @doc """ -Generates a line plot using the provided `x` and `y` data points. -##Parameters -`x`: A list of x-coordinates for the data points (e.g., [1, 2, 3]). -`y`: A list of y-coordinates corresponding to x (e.g., [2, 4, 6]). -`opts`: it also expect some plot specific options such as - `color`: line color - `linestyle`: line style - `marker`: marker style - -### Example - -```elixir -# Define the data points -x = [1, 2, 3, 4, 6, 6, 7] -y = [1, 3, 4, 4, 5, 6, 7] - -# Specify plot configurations -frame_width = 6 -frame_height = 6 -size = {frame_width, frame_height} -margin = 0.05 -font_size = "16pt" -title_font_size = "18pt" -ticks = [1, 2, 3, 4, 5, 6, 7] - -# Create and configure the plot -x -|> Matplotex.plot(y) # Create a line plot -|> Matplotex.figure(%{ # Configure the figure - figsize: size, - margin: margin - }) -""" + @doc """ + Generates a line plot using the provided `x` and `y` data points. + ##Parameters + `x`: A list of x-coordinates for the data points (e.g., [1, 2, 3]). + `y`: A list of y-coordinates corresponding to x (e.g., [2, 4, 6]). + `opts`: it also expect some plot specific options such as + `color`: line color + `linestyle`: line style + `marker`: marker style + + ### Example + + ```elixir + # Define the data points + x = [1, 2, 3, 4, 6, 6, 7] + y = [1, 3, 4, 4, 5, 6, 7] + + # Specify plot configurations + frame_width = 6 + frame_height = 6 + size = {frame_width, frame_height} + margin = 0.05 + font_size = "16pt" + title_font_size = "18pt" + ticks = [1, 2, 3, 4, 5, 6, 7] + + # Create and configure the plot + x + |> Matplotex.plot(y) # Create a line plot + |> Matplotex.figure(%{ # Configure the figure + figsize: size, + margin: margin + }) + """ @spec plot(list(), list()) :: Figure.t() def plot(x, y) when is_list(x) and is_list(y) do plot(x, y, []) @@ -360,6 +364,40 @@ x LinePlot.create(%Figure{axes: %LinePlot{}}, {x, y}, opts) end + @doc """ + Adds an additional dataset to a figure for creating multi-line plots. + + This function allows you to overlay multiple datasets onto a single figure, enabling the creation of multi-line plots for better data visualization and comparison. + + ## Parameters + + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `x` (list): A list of x-coordinates for the new dataset. + - `y` (list): A list of y-coordinates corresponding to the x-coordinates. + + ## Usage + + Use this function when you need to generate multi-line plots with multiple datasets. Below is an example of its usage: + + ```elixir + # X-axis values + x = [1, 2, 3, 4, 5] + + # Dataset 1 + y1 = [1, 4, 9, 16, 25] + + # Dataset 2 + y2 = [1, 3, 6, 10, 15] + + # Dataset 3 + y3 = [2, 5, 7, 12, 17] + + # Plotting multiple datasets on the same figure + x + |> Matplotex.plot(y1, color: "blue", linestyle: "_", marker: "o", label: "Dataset 1") + |> Matplotex.plot(x, y2, color: "red", linestyle: "--", marker: "^", label: "Dataset 2") + |> Matplotex.plot(x, y3, color: "green", linestyle: "-.", marker: "s", label: "Dataset 3") + """ def plot(%Figure{} = figure, x, y, opts) do figure |> show_legend() @@ -367,27 +405,160 @@ x end @doc """ - Creates a histogram with given data and bins. + Creates a histogram with the given data and bins. - ## Examples + This function generates a histogram visualization using the provided data, number of bins, and additional plot configuration options. + ## Parameters + - `data`: list of random distributions of numbers + - `bins`: number of bins to get the density of each distribution + - `opts` (keyword list, optional): additional plot configuration options, such as, color, edge color, alpha + ## Examples - iex> Matplotex.hist([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5) + ```elixir + # Generate a list of random values from a normal distribution + values = + Nx.Random.key(12) + |> Nx.Random.normal(0, 1, shape: {1000}) + |> elem(0) + |> Nx.to_list() + + # Specify the number of bins for the histogram + bins = 30 + + # Create the histogram with labels, title, and styling + Matplotex.hist( + values, + bins, + x_label: "Value", + y_label: "Frequency", + title: "Histogram", + color: "blue", + edge_color: "black", + alpha: 0.7, + x_ticks_count: 9 + ) """ def hist(data, bins), do: hist(data, bins, []) + @doc """ + Adds an additional dataset to a histogram plot. + + This function allows you to overlay multiple datasets on the same figure, enabling the creation of combined histograms for comparative analysis. + + ## Parameters + + - `figure` (%Figure{}): The figure to which the new dataset will be added. + - `data` (list): A list of random numbers representing the distribution to be added. + - `bins` (integer): The number of bins to divide the dataset into for density calculation. + + ## Usage + + Use this function when you need to generate multiple histograms from different datasets on the same figure. This is particularly useful for comparing distributions. + + ## Example + + ```elixir + # Generate the first dataset + values1 = + Nx.Random.key(12) + |> Nx.Random.normal(0, 1, shape: {1000}) + |> elem(0) + |> Nx.to_list() + + # Generate the second dataset + values2 = + Nx.Random.key(13) + |> Nx.Random.normal(0, 1, shape: {500}) + |> elem(0) + |> Nx.to_list() + + # Define the number of bins + bins = 30 + + # Create a histogram with multiple datasets + Matplotex.hist(values1, bins, + x_label: "Value", + y_label: "Frequency", + title: "Histogram", + color: "blue", + edge_color: "black", + alpha: 0.7, + x_ticks_count: 9 + ) + |> Matplotex.hist(values2, bins, color: "red") + """ + def hist(%Figure{} = figure, data, bins) do + Histogram.create(figure, {data, bins}, []) + end + def hist(data, bins, opts) do Histogram.create(%Figure{axes: %Histogram{}}, {data, bins}, opts) end + def hist(%Figure{} = figure, data, bins, opts) do + Histogram.create(figure, {data, bins}, opts) + end + + @doc """ + Generates a spline graph based on the provided datasets. + + This function creates a smooth curve that connects the data points (x, y) using spline interpolation, ideal for visualizing continuous relationships between two variables. + + ## Parameters + + - `x` (list): A list of x-coordinates for the data points. + - `y` (list): A list of y-coordinates corresponding to the x-coordinates. + - `opts` (keyword list, optional): A list of additional configuration options for the plot. These can include attributes like `color`, `line_style`, `marker`, `x_label`, `y_label`, and more. + + ## Example + + ```elixir + # Generate x and y data + x_nx = Nx.linspace(0, 10, n: 100) + x = Nx.to_list(x_nx) + y = x_nx |> Nx.sin() |> Nx.to_list() + + # Plot a spline graph with optional configurations + Matplotex.spline(x, y, x_label: "X", y_label: "Y", edge_color: "green") + """ def spline(x, y), do: spline(x, y, []) def spline(x, y, opts), do: spline(%Figure{axes: %Spline{}}, x, y, opts) + @doc """ + Adds an additional spline to an existing spline graph. + + This function allows you to extend an existing spline plot by adding another spline with different data points. It is ideal for comparing multiple datasets on the same plot. + + ## Parameters + + - `figure` (%Figure{}): The existing figure (spline graph) to which the new spline will be added. + - `x` (list): A list of x-coordinates for the new spline. + - `y` (list): A list of y-coordinates corresponding to the x-coordinates for the new spline. + - `opts` (keyword list, optional): Additional configuration options for the plot. This can include attributes like: + - `color`: The color of the spline. + - `line_style`: The style of the line (e.g., dashed, solid, etc.). + - `marker`: Marker style for the data points (e.g., circle, square, etc.). + - `label`: Label for the new spline. + + ## Example + + ```elixir + # Generate x and y data + x_nx = Nx.linspace(0, 10, n: 100) + x = Nx.to_list(x_nx) + y1 = x_nx |> Nx.sin() |> Nx.to_list() + y2 = x_nx |> Nx.cos() |> Nx.to_list() + + # Create an initial spline and add another one + Matplotex.spline(x, y1, x_label: "X", y_label: "Y", edge_color: "green") + |> Matplotex.spline(x, y2, x_label: "X", y_label: "Y", edge_color: "red") + """ def spline(%Figure{} = figure, x, y, opts) do Spline.create(figure, {x, y}, opts) end @doc """ - Sets X and Y labels for the graph with given font details + Sets X labels for the graph with given font details ## Examples diff --git a/lib/matplotex/element/polygon.ex b/lib/matplotex/element/polygon.ex index 47747a0..2d81010 100644 --- a/lib/matplotex/element/polygon.ex +++ b/lib/matplotex/element/polygon.ex @@ -15,7 +15,6 @@ defmodule Matplotex.Element.Polygon do ) end - defp assemble_point(%{points: point}) do for {x, y} <- point do "#{to_pixel(x)},#{to_pixel(y)} " diff --git a/lib/matplotex/figure/areal/histogram.ex b/lib/matplotex/figure/areal/histogram.ex index ce60acc..b51e37f 100644 --- a/lib/matplotex/figure/areal/histogram.ex +++ b/lib/matplotex/figure/areal/histogram.ex @@ -24,17 +24,20 @@ defmodule Matplotex.Figure.Areal.Histogram do @impl Areal def create( - %Figure{axes: %__MODULE__{} = axes, rc_params: rc_params} = figure, + %Figure{axes: %__MODULE__{dataset: datasets} = axes, rc_params: rc_params} = figure, {data, bins}, opts ) do {x, y} = bins_and_hists(data, bins) dataset = Dataset.cast(%Dataset{x: x, y: y}, opts) + datasets = datasets ++ [dataset] + + xydata = flatten_for_data(datasets) %Figure{ figure - | axes: %__MODULE__{axes | data: {x, y}, dataset: [dataset]}, + | axes: %__MODULE__{axes | data: xydata, dataset: datasets}, rc_params: %RcParams{rc_params | y_padding: @make_it_zero} } |> PlotOptions.set_options_in_figure(opts) diff --git a/lib/matplotex/figure/areal/spline.ex b/lib/matplotex/figure/areal/spline.ex index 30ea4a5..1e6ac9b 100644 --- a/lib/matplotex/figure/areal/spline.ex +++ b/lib/matplotex/figure/areal/spline.ex @@ -8,7 +8,6 @@ defmodule Matplotex.Figure.Areal.Spline do alias Matplotex.Figure.TwoD alias Matplotex.Figure - use Areal frame( @@ -21,6 +20,7 @@ defmodule Matplotex.Figure.Areal.Spline do region_legend: %Region{}, region_content: %Region{} ) + @impl Areal def create( %Figure{axes: %__MODULE__{dataset: data} = axes} = figure, @@ -40,29 +40,34 @@ defmodule Matplotex.Figure.Areal.Spline do @impl Areal def materialize(figure) do - figure - |>__MODULE__.materialized_by_region() - |> materialize_spline() - + figure + |> __MODULE__.materialized_by_region() + |> materialize_spline() end - defp materialize_spline(%Figure{axes: - %{ - dataset: data, - limit: %{x: xlim, y: ylim}, - region_content: %Region{ - x: x_region_content, - y: y_region_content, - width: width_region_content, - height: height_region_content - }, - element: elements - } = axes, - rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding}} = figure) do + + defp materialize_spline( + %Figure{ + axes: + %{ + dataset: data, + limit: %{x: xlim, y: ylim}, + region_content: %Region{ + x: x_region_content, + y: y_region_content, + width: width_region_content, + height: height_region_content + }, + element: elements + } = axes, + rc_params: %RcParams{x_padding: x_padding, y_padding: y_padding} + } = figure + ) do x_padding_value = width_region_content * x_padding y_padding_value = height_region_content * y_padding shrinked_width_region_content = width_region_content - x_padding_value * 2 shrinked_height_region_content = height_region_content - y_padding_value * 2 transition = {x_region_content + x_padding_value, y_region_content + y_padding_value} + line_elements = data |> Enum.map(fn dataset -> @@ -83,19 +88,33 @@ defmodule Matplotex.Figure.Areal.Spline do %Figure{figure | axes: %{axes | element: elements}} end - - - defp capture(%Dataset{transformed: transformed, color: color,edge_color: edge_color, line_width: stroke_width}, move_to_def) do + defp capture( + %Dataset{ + transformed: transformed, + color: color, + edge_color: edge_color, + line_width: stroke_width + }, + move_to_def + ) do {moveto, transformed} = List.pop_at(transformed, 0, move_to_def) cubic = Enum.slice(transformed, 0..2) smooths = blend(transformed, 3) - %Spline{type: "figure.spline", moveto: moveto, cubic: cubic, smooths: smooths, fill: color, stroke: edge_color, stroke_width: stroke_width} + + %Spline{ + type: "figure.spline", + moveto: moveto, + cubic: cubic, + smooths: smooths, + fill: color, + stroke: edge_color, + stroke_width: stroke_width + } end defp blend(smooths, start_from) do - smooths - |>Enum.slice(start_from..-1//1) - |>Enum.chunk_every(2) - + smooths + |> Enum.slice(start_from..-1//1) + |> Enum.chunk_every(2) end end diff --git a/lib/matplotex/figure/two_d.ex b/lib/matplotex/figure/two_d.ex index 74b2496..6021936 100644 --- a/lib/matplotex/figure/two_d.ex +++ b/lib/matplotex/figure/two_d.ex @@ -1,11 +1,11 @@ defmodule Matplotex.Figure.TwoD do defstruct [:x, :y] - def update(twod, opts, context) do + def update(%__MODULE__{x: x, y: y} = twod, opts, context) do %__MODULE__{ twod - | x: fetch_from_opts(opts, :x, context), - y: fetch_from_opts(opts, :y, context) + | x: fetch_from_opts(opts, :x, context) || x, + y: fetch_from_opts(opts, :y, context) || y } end diff --git a/lib/matplotex/helpers.ex b/lib/matplotex/helpers.ex index cc9fee8..08ef9ea 100644 --- a/lib/matplotex/helpers.ex +++ b/lib/matplotex/helpers.ex @@ -325,15 +325,46 @@ defmodule Matplotex.Helpers do |> copy() end + def multi_hist() do + values1 = + Nx.Random.key(12) |> Nx.Random.normal(0, 1, shape: {1000}) |> elem(0) |> Nx.to_list() + + bins = 30 + values2 = Nx.Random.key(13) |> Nx.Random.normal(0, 1, shape: {500}) |> elem(0) |> Nx.to_list() + + Matplotex.hist(values1, bins, + x_label: "Value", + y_label: "Frequency", + title: "Histogram", + color: "blue", + edge_color: "black", + alpha: 0.7, + x_ticks_count: 9 + ) + |> Matplotex.hist(values2, bins, color: "red") + |> Matplotex.show() + |> copy() + end + def spline() do x_nx = Nx.linspace(0, 10, n: 100) x = Nx.to_list(x_nx) y = x_nx |> Nx.sin() |> Nx.to_list() - Matplotex.spline(x, y, x_label: "X", y_label: "Y", edge_color: "green") |> Matplotex.show() |> copy() end + def multi_spline() do + x_nx = Nx.linspace(0, 10, n: 100) + x = Nx.to_list(x_nx) + y1 = x_nx |> Nx.sin() |> Nx.to_list() + y2 = x_nx |> Nx.cos() |> Nx.to_list() + + Matplotex.spline(x, y1, x_label: "X", y_label: "Y", edge_color: "green") + |> Matplotex.spline(x, y2, x_label: "X", y_label: "Y", edge_color: "red") + |> Matplotex.show() + |> copy() + end end diff --git a/test/matplotex/figure/areal/spline_test.exs b/test/matplotex/figure/areal/spline_test.exs index c7cd645..12d43cd 100644 --- a/test/matplotex/figure/areal/spline_test.exs +++ b/test/matplotex/figure/areal/spline_test.exs @@ -2,18 +2,18 @@ defmodule Matplotex.Figure.Areal.SplineTest do use Matplotex.PlotCase alias Matplotex.Figure alias Matplotex.Figure.Areal.Spline - setup do - x_nx = Nx.linspace(0, 10, n: 100) - x = Nx.to_list(x_nx) - y = x_nx |> Nx.sin() |> Nx.to_list() - figure = Matplotex.spline(x, y, x_label: "X", y_label: "Y") - {:ok, %{figure: figure}} - end + setup do + x_nx = Nx.linspace(0, 10, n: 100) + x = Nx.to_list(x_nx) + y = x_nx |> Nx.sin() |> Nx.to_list() + figure = Matplotex.spline(x, y, x_label: "X", y_label: "Y") + {:ok, %{figure: figure}} + end - test "adds a spline element in a figure",%{figure: figure} do - assert %Figure{axes: %Spline{element: elements}} =Spline.materialize(figure) - assert Enum.any?(elements, &(&1.type=="figure.spline")) - end + test "adds a spline element in a figure", %{figure: figure} do + assert %Figure{axes: %Spline{element: elements}} = Spline.materialize(figure) + assert Enum.any?(elements, &(&1.type == "figure.spline")) + end end From ff326d43ec081230314cb4d19f7d12df48716199 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 9 Jan 2025 15:36:54 +0530 Subject: [PATCH 4/5] patch --- lib/matplotex.ex | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index 37276f8..4fa06b0 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -212,12 +212,6 @@ defmodule Matplotex do |> BarChart.create({pos, values, width}, opts) end - @doc """ - Creates a scatter plot based on the given data - """ - def scatter(stream, opts) when is_struct(stream, Stream) do - Scatter.create(stream, opts) - end @doc """ Creates a scatter plot based on the given `x` and `y` values, with optional customization provided via `opts`. @@ -678,28 +672,7 @@ defmodule Matplotex do """ - # @spec legend(Figure.t(), keyword() | map()) :: Figure.t() - # def legend(figure, opts) when is_map(opts) do - # Figure.add_legend(figure, opts) - # end - - # def legend(figure, [h | _t] = opts) when is_tuple(h) do - # params = Enum.into(opts, %{}) - # Figure.add_legend(figure, params) - # end - - # def legend(figure, labels) when is_list(labels) do - # Figure.add_legend(figure, %{labels: labels}) - # end - @doc """ - Function to update figure params - ## Examples - - iex> Matplotex.figure(figure, figsize: {10,6}, margin: 0.1) - %Matplotex.Figure{} - """ - @deprecated def figure(figure, params) do Figure.update_figure(figure, params) end From ebbb43a3903d73b20e8fdb3fc97f2bcf3e3824b4 Mon Sep 17 00:00:00 2001 From: Mohammed Sadique Date: Thu, 9 Jan 2025 15:57:38 +0530 Subject: [PATCH 5/5] module doc --- lib/matplotex.ex | 81 +++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/lib/matplotex.ex b/lib/matplotex.ex index 4fa06b0..c8d0d1b 100644 --- a/lib/matplotex.ex +++ b/lib/matplotex.ex @@ -2,25 +2,19 @@ defmodule Matplotex do @moduledoc """ # Matplotex - a lightweight and efficient library for Elixir projects that facilitates server-side - SVG generation for data visualization. Designed to integrate seamlessly with Phoenix LiveView, - it serves as a powerful tool for creating dynamic visualizations in web applications. - - it supports the following graphs - - Line plots - - Bar charts - - Pie charts - - Spline graphs - - Histograms - - Scatter plots - - The plotting of a graph comes with set of common parameters and set of plot specific parameters - all of them will share with the corresponding function documentation, this section covers one examaple - as a line plot. - There are two approach to generate plots - - by using specific function to set parameters - - by using parameters along with options + A lightweight and efficient Elixir library designed for server-side SVG generation, ideal for data visualization in web applications. It integrates seamlessly with Phoenix LiveView and provides powerful tools for generating dynamic visualizations. + ## Supported Graph Types + +Matplotex supports the following types of visualizations: + +- Line Plots +- Bar Charts +- Pie Charts +- Spline Graphs +- Histograms +- Scatter Plots +## Examples ```elixir alias Matplotex as: M @@ -55,8 +49,7 @@ defmodule Matplotex do ) |> M.show() ``` - This module exposes all of the functions for setters - and another approach is creating plots by using plot options the code is as follows + ```elixir alis Matplotex as: M x = [1, 2, 3, 4, 6, 6, 7] @@ -90,37 +83,39 @@ defmodule Matplotex do ) |> M.show() ``` - just for simplicity and convenience of the user it is keeping both patterns, no difference on using one on another + **Note**: Both approaches are equivalent, and users can choose whichever pattern is more convenient. + + +## Runtime Configuration (RC Params) +The function M.set_rc_params/2 allows you to set runtime configuration parameters for the plot, such as font sizes, colors, and line styles. These settings affect visual elements like the font style and size for the labels, ticks, and title. - So the user has the control on the all parameters on the inner elements of the chart +By default, the plot starts with standard values (e.g., Arial or Verdana font, size 12). You can modify these using the RC parameters. + +### Available RC Params: +line_width, line_style +grid_color, grid_linestyle, grid_alpha +tick_line_length +x_padding, y_padding, padding +legend_width +Refer to specific plot documentation for detailed information on padding usage. - ## Rc Params - In the first example along with the setter functions you might noticed M.set_rc_params/2 - The role of this function is similar to other functions we are keeping some values with the plot data - and the acronym RC stands for Runtime configuration, the plot data holds the labels limits ticks, etc - The RC params are holding the font size, color, style etc, by defaul one chart object kickstart with some default values - just for the sake of it needed some values, by default all the fonts are Areal, Veradana, sans-serif and using standard font size 12 - if a user creates a plots with no inputs for any of these the plot will be choosing the default values - besides font configuration Rc params covers - `line_width, line_style, grid_color, grid_linestyle, grid_alpha, tick_line_length, x_padding, y_padding, legend_width` - There is two types of padding `x_padding, y_padding` and `padding` the perfect use of those can be found on upcoming plot specific documentations ## Elements The output format of the plot is SVG will support more formats in future, anyway the svg is a group of some elements put together, throught the execution it is generating those elements through elixir data structure, all element data structure contains some svg equivalent data that converts the elements to SVG string, the output SVG string can be used directly in the web application. - ## Figure - The execution a plot carriec out by a data structure named Matplotex.Figure it holds all the adequate information to generate a figure it containst the keys - `:figsize` - is a tuple carries width and height of the figure eg: {10,6} - `:axes` - is another object that will varie according to the plot - `:rc_params` - the runtime configurations - `:margin` - the margin of the figure - - ## M.show/1 - All examples above using this function `M.show/1` after a plot generation API call - The all APIs from this module is ment to return an svg equivalent Data Matplotex.Figure with distinct object associated with the `axes` key, so to convert that data to - an SVG chart use `M.show/1` +## Figure Data Structure +The main data structure used to generate a plot is Matplotex.Figure, which contains all the necessary information for the plot, including: + +- `:figsize`: A tuple specifying the width and height of the figure (e.g., {10, 6}). +- `:axes`: Contains the axes data, which varies depending on the plot type. +- `:rc_params`: The runtime configuration parameters. +- `:margin`: Specifies the margin of the figure. + +## `M.show/1` +After creating a figure using the functions provided, call M.show/1 to generate and display the final SVG representation of the plot. The show/1 function will convert the Matplotex.Figure data into a valid SVG string. + """ alias Matplotex.Figure.Areal.Spline