Skip to content

Commit f73b15c

Browse files
Merge pull request #42 from thomas-saigre/40-reduce-size
40 reduce size
2 parents 600f88f + 2ea1436 commit f73b15c

17 files changed

Lines changed: 325 additions & 57 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
fail-fast: false
2626
matrix:
2727
os: [ubuntu-latest, macOS-latest]
28-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
28+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
2929
steps:
3030
- uses: actions/setup-python@v3
3131
with:
@@ -38,7 +38,7 @@ jobs:
3838
pip install tox
3939
tox -- --cov tikzplotly --cov-report xml --cov-report term
4040
- uses: codecov/codecov-action@v5.4.3
41-
if: ${{ matrix.python-version == '3.13' && matrix.os == 'ubuntu-latest' }}
41+
if: ${{ matrix.python-version == '3.14' && matrix.os == 'ubuntu-latest' }}
4242
with:
4343
token: ${{ secrets.CODECOV_TOKEN }}
4444
verbose: true

.github/workflows/documentation.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
name: Build GitHub Pages
22
on:
3-
push:
4-
branches:
5-
- main
6-
paths:
7-
- 'docs/**'
3+
release:
4+
types: [published]
85
workflow_dispatch:
96
permissions:
107
contents: write
@@ -22,7 +19,7 @@ jobs:
2219
fetch-depth: 0
2320
- uses: actions/setup-python@v5
2421
with:
25-
python-version: 3.9
22+
python-version: 3.14
2623
- run: pip install \
2724
mkdocs-material[recommended] mkdocs-git-revision-date-localized-plugin
2825
- run: mkdocs gh-deploy --config-file mkdocs.yml --force

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Tikzplotly
1717

18-
Convert [plotly](https://plotly.com/python/) figures to TikZ code for inclusion into [PGFPlots](https://www.ctan.org/pkg/pgfplots) ([PGF/TikZ](https://www.ctan.org/pkg/pgf)) figures.
18+
Convert [Plotly](https://plotly.com/python/) figures to TikZ code for inclusion into [PGFPlots](https://www.ctan.org/pkg/pgfplots) ([PGF/TikZ](https://www.ctan.org/pkg/pgf)) figures.
1919

2020
This results in a ti*k*z code, that can be easily included into your LaTeX document.
2121
This also allows to easily edit the content of the figure.
@@ -101,9 +101,9 @@ To correctly compile the document, you will need to add the following packages t
101101

102102
## Note
103103

104-
* This module is in development and new features are added bit by bit, when needed. If you have a feature request, please open an issue with the plotly figure you want to convert and the desired output.
104+
* This module is in development and new features are added bit by bit, when needed. If you have a feature request, please open an issue with the Plotly figure you want to convert and the desired output.
105105
You can also submit a pull request with the desired feature !
106-
* Some feature can result in different output between the plotly figure and the TikZ figure, for instance the size of markers, more details can be found in [the documentation](https://thomas-saigre.github.io/tikzplotly/).
106+
* Some feature can result in different output between the Plotly figure and the TikZ figure, for instance the size of markers, more details can be found in [the documentation](https://thomas-saigre.github.io/tikzplotly/).
107107

108108

109109
## References

docs/plot/NB.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# Some differences between plotly ans pgfplots
1+
# Some differences between Plotly ans pgfplots
22

3-
We gather here some points that are different between a plotly figure and the corresponding Ti*k*Z figure generated with `tikzplotly`.
3+
We gather here some points that are different between a Plotly figure and the corresponding Ti*k*Z figure generated with `tikzplotly`.
44

5-
* Size of the objects: in plotly, the size is given in `px` unit, while in Ti*k*Z it is given in `pt`. A conversion is performed (`1 px = 0.75 pt`), but this still results in objects of different sizes.
6-
* By default, the colors or the markers are not the same in plotly and pgfplots. For instance, if nothing is specified, plotly will always use a dot marker, while pgfplot will change for each trace.
7-
* The order of displaying the traces may be unconsistent between plotly and pgfplots. For instance, for [this example](https://plotly.com/python/histograms/#several-histograms-for-the-different-values-of-one-column), the two traces are inverted.
8-
* The angle of rotation is different between Plotly and Ti*k*Z, but the function Plotly ↦ Ti*k*Z is not know at this current point.
9-
* When tricky names are used in symbolic expression (such as names with a space within), the space is removed by tisk plotly (*e.g.* the text `United Kingdom` in Plotly will be exported as `UnitedKindgon` in Ti*k*Z), fill free to update the exported file to render the figure you wish!
5+
* Size of the objects: in Plotly, the size is given in `px` unit, while in Ti*k*Z it is given in `pt`. A conversion is performed (`1 px = 0.75 pt`), but this still results in objects of different sizes.
6+
* By default, the colors or the markers are not the same in Plotly and pgfplots. For instance, if nothing is specified, Plotly will always use a dot marker, while pgfplots will change for each trace.
7+
* The order of displaying the traces may be inconsistent between Plotly and pgfplots. For instance, for [this example](https://Plotly.com/python/histograms/#several-histograms-for-the-different-values-of-one-column), the two traces are inverted.
8+
* The angle of rotation is different between Plotly and Ti*k*Z, but the function Plotly ↦ Ti*k*Z is not known at this current point.
9+
* When tricky names are used in symbolic expression (such as names with a space within), the space is removed by tikzplotly (*e.g.* the text `United Kingdom` in Plotly will be exported as `UnitedKindgon` in Ti*k*Z), fill free to update the exported file to render the figure you wish!

docs/plot/features.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
From [Styling Markers in Python](https://plotly.com/python/marker-style/).
88

9-
??? example "Marker Style Example"
9+
??? Example "Marker Style Example"
10+
1011
```python
1112
import plotly.express as px
1213
import tikzplotly
@@ -26,6 +27,6 @@ From [Styling Markers in Python](https://plotly.com/python/marker-style/).
2627

2728

2829
!!! Note
29-
- There are somes markers implemented in plotly that are not available in pgfplots (or at least not referenced in the [documentation](https://tikz.dev/pgfplots/reference-markers)). For more details, refer to the example [test_markers](https://github.com/thomas-saigre/tikzplotly/blob/main/tests/test_markers.py). By defualt, the marker style `*` will be used.
30-
- By default, the colors or the markers are not the same in plotly and pgfplots. For instance, if nothing is specified, plotly will always use a dot marker, while pgfplot will change for each trace.
30+
- There are some markers implemented in Plotly that are not available in pgfplots (or at least not referenced in the [documentation](https://tikz.dev/pgfplots/reference-markers)). For more details, refer to the example [test_markers](https://github.com/thomas-saigre/tikzplotly/blob/main/tests/test_markers.py). By default, the marker style `*` will be used.
31+
- By default, the colors or the markers are not the same in Plotly and pgfplots. For instance, if nothing is specified, Plotly will always use a dot marker, while pgfplots will change for each trace.
3132
- The angle of rotation is different between Plotly and Ti*k*Z.

docs/plot/supported.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The plots type supported by tikzplotly are presented in this page.
1313

1414
The code has been constructed to export (almost all) the figures of the page [Line Charts in Python](https://plotly.com/python/line-charts/) of Plotly documentation.
1515

16-
??? example "Scatter plot Example"
16+
??? Example "Scatter plot Example"
1717

1818
```python
1919
import plotly.express as px
@@ -31,7 +31,8 @@ The code has been constructed to export (almost all) the figures of the page [Li
3131

3232
The code has been constructed to export (almost all) the figures of the page [Heatmaps in Python](https://plotly.com/python/heatmaps/) of Plotly documentation.
3333

34-
??? example "Heatmap Example"
34+
??? Example "Heatmap Example"
35+
3536
```python
3637
import plotly.express as px
3738
import tikzplotly
@@ -44,14 +45,15 @@ The code has been constructed to export (almost all) the figures of the page [He
4445
![Heatmap Example](../assets/examples/heatmap.png)
4546

4647
!!! Note
47-
- If possible, TikzPlotly try to save the heatmap as a png of the smallest size possible, namely 1 pixel for each value of the heatmap. But in some case, such export does not work. In this case, the image is saved in the original size of the Plotly figure.
48+
- If possible, TikzPlotly try to save the heatmap as a PNG of the smallest size possible, namely 1 pixel for each value of the heatmap. But in some case, such export does not work. In this case, the image is saved in the original size of the Plotly figure.
4849

4950

5051
## Histograms
5152

5253
The examples of the page [Histograms in Python](https://plotly.com/python/histograms/) of Plotly documentation are supported.
5354

54-
??? example "Histogram Example"
55+
??? Example "Histogram Example"
56+
5557
```python
5658
df = px.data.tips()
5759
fig = px.histogram(df, x="total_bill")
@@ -68,7 +70,8 @@ The examples of the page [Histograms in Python](https://plotly.com/python/histog
6870

6971
Some examples of the page [Bar Charts in Python](https://plotly.com/python/bar-charts/) of Plotly documentation are supported (not stacked and aggregated bars).
7072

71-
??? example "Bar plot"
73+
??? Example "Bar plot"
74+
7275
```python
7376
wide_df = px.data.medals_wide()
7477
fig = px.bar(
@@ -88,7 +91,8 @@ Some examples of the page [Bar Charts in Python](https://plotly.com/python/bar-c
8891
The examples from the pages [Polar Charts in Python](https://plotly.com/python/polar-chart/) and [Radar Charts in Python](https://plotly.com/python/radar-chart/) are supported.
8992

9093

91-
??? example "Polar plot"
94+
??? Example "Polar plot"
95+
9296
```python
9397
df = px.data.wind()
9498
fig = px.line_polar(df, r="frequency", theta="direction", color="strength", line_close=True,
@@ -99,7 +103,8 @@ The examples from the pages [Polar Charts in Python](https://plotly.com/python/p
99103
![Polar plot Example](../assets/examples/polar.png)
100104

101105

102-
??? example "Radar plot"
106+
??? Example "Radar plot"
107+
103108
```python
104109
df = pd.DataFrame(dict(
105110
r=[1, 5, 2, 2, 3],
@@ -116,7 +121,8 @@ The examples from the pages [Polar Charts in Python](https://plotly.com/python/p
116121
Examples from [3D Scatter Plots in Python ](https://plotly.com/python/3d-scatter-plots/) can be exported with tikzplotly.
117122

118123

119-
??? example "3D scatter plot"
124+
??? Example "3D scatter plot"
125+
120126
```python
121127
df = px.data.iris()
122128
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width', color='species')

docs/plot/usage.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ tikzplotly.save("example.tex", fig)
1111

1212
The arguments of the function `tikzplotly.save` are:
1313

14-
* `filename` (str): The name of the file where the ti*k*z code will be saved.
15-
* `fig` (plotly.graph_objs.Figure): The figure to be saved.
16-
* `tikz_options` (str, optional): The options to be passed to the `tikzpicture` environment. Default is `None`.
14+
* `filename` (`str`): The name of the file where the ti*k*z code will be saved.
15+
* `fig` (`plotly.graph_objs.Figure`): The figure to be saved.
16+
* `tikz_options` (`str`, optional): The options to be passed to the `tikzpicture` environment. Default is `None`.
1717
For example `tikz_options="scale=0.5"` will scale the figure by a factor 0.5.
18-
* `axis_options` (str, optional): Option that you would like to manually add the the `axis` environment.
19-
* `include_disclamer` (bool, optional): If `True`, the line `% This file was created with tikzplotly version XXX.` is added at the head of the generated code. Default is `True`.
20-
* `img_name` (str, optional): only for the export of [heatmaps](supported.md#heat-maps), the name of the image that will be saved. Default is `heatmap.png`.
18+
* `axis_options` (`str`, optional): Option that you would like to manually add to the `axis` environment.
19+
* `include_disclamer` (`bool`, optional): If `True`, the line `% This file was created with tikzplotly version XXX.` is added at the head of the generated code. Default is `True`.
20+
* `img_name` (`str`, optional): only for the export of [heatmaps](supported.md#heat-maps), the name of the image that will be saved. Default is `heatmap.png`.
21+
* `decimate` (`int|None`, optional): only for the exports of scatter plots (in [2D](supported.md#scatter-plots) and [3D](supported.md#3d-scatter-plots)), reduce the number of data exported to only get the `decimate`-th ones (and the last one). If `None`, then all the data are exported. This is the default behavior.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ classifiers = [
1313
"Development Status :: 5 - Production/Stable",
1414
"Programming Language :: Python",
1515
"Programming Language :: Python :: 3",
16-
"Programming Language :: Python :: 3.9",
1716
"Programming Language :: Python :: 3.10",
1817
"Programming Language :: Python :: 3.11",
1918
"Programming Language :: Python :: 3.12",
2019
"Programming Language :: Python :: 3.13",
20+
"Programming Language :: Python :: 3.14",
2121
"License :: OSI Approved :: MIT License",
2222
"Operating System :: OS Independent",
2323
"Topic :: Multimedia :: Graphics :: Graphics Conversion",

src/tikzplotly/_data_container.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class Data:
4646
"""Class to handle data in TikZ plots.
4747
"""
4848

49-
def __init__(self, name, x):
49+
def __init__(self, name, x, decimate: int|None=None):
5050
"""Initialize a Data object.
5151
5252
Parameters
@@ -59,6 +59,8 @@ def __init__(self, name, x):
5959
self.name = name
6060
self.macro_name = "\\" + replace_all_digits(name)
6161
self.x = x
62+
# decimate: None means keep all points, otherwise integer >= 1
63+
self.decimate = decimate
6264
self.y_label = []
6365
self.y_data = []
6466

@@ -84,7 +86,7 @@ def add_y_data(self, y, y_label=None):
8486
class Data3D:
8587
"""Handle 3D data in Tikz plots
8688
"""
87-
def __init__(self, x, y, z, name):
89+
def __init__(self, x, y, z, name, decimate: int|None=None):
8890
"""Initialize the Data3D object
8991
9092
Parameters
@@ -101,6 +103,8 @@ def __init__(self, x, y, z, name):
101103
self.x = np.array(x)
102104
self.y = np.array(y)
103105
self.z = np.array(z)
106+
# decimate: None means keep all points, otherwise integer >= 1
107+
self.decimate = decimate
104108
if name:
105109
self.name = sanitize_text(name, keep_space=0)
106110
else:
@@ -128,7 +132,7 @@ class DataContainer:
128132
def __init__(self):
129133
self.data = []
130134

131-
def add_data(self, x, y, name=None, y_label=None):
135+
def add_data(self, x, y, name=None, y_label=None, decimate: int|None=None):
132136
"""Add data to the container.
133137
134138
Parameters
@@ -141,11 +145,16 @@ def add_data(self, x, y, name=None, y_label=None):
141145
name of the y data, by default None
142146
name, optional
143147
name of the data, by default None
148+
decimate, optional
149+
number of data to keep, all of them if None, by default None.
144150
145151
Returns
146152
-------
147153
tuple (macro_name, y_label), where macro_name is the name of the data in LaTeX and y_label the name of the y data in LaTeX
148154
"""
155+
if decimate is not None and (not isinstance(decimate, int) or decimate < 1):
156+
raise ValueError("decimate must be None or an integer >= 1")
157+
149158
for data in self.data:
150159
if len(data.x) != len(x):
151160
continue
@@ -157,12 +166,12 @@ def add_data(self, x, y, name=None, y_label=None):
157166
elif hasattr(are_equals, "all") and are_equals.all():
158167
y_label_val = data.add_y_data(y, y_label or name)
159168
return data.macro_name, treat_data(y_label_val)
160-
data_to_add = Data(f"data{index_to_letters(len(self.data))}", x)
169+
data_to_add = Data(f"data{index_to_letters(len(self.data))}", x, decimate=decimate)
161170
y_label_val = data_to_add.add_y_data(y, y_label or name)
162171
self.data.append(data_to_add)
163172
return data_to_add.macro_name, treat_data(y_label_val)
164173

165-
def add_data3d(self, x, y, z, name=None):
174+
def add_data3d(self, x, y, z, name=None, decimate: int|None=None):
166175
"""Add data to the container.
167176
168177
Parameters
@@ -175,16 +184,21 @@ def add_data3d(self, x, y, z, name=None):
175184
z values of the data
176185
name, optional
177186
name of the data, by default None
187+
decimate, optional
188+
number of data to keep, all of them if None, by default None.
178189
179190
Returns
180191
-------
181192
tuple (macro_name, z_name), where macro_name is the name of the data in LaTeX and z_name the name of the z data in LaTeX
182193
"""
194+
if decimate is not None and (not isinstance(decimate, int) or decimate < 1):
195+
raise ValueError("decimate must be None or an integer >= 1")
196+
183197
for data in self.data:
184198
if hasattr(data, "x") and hasattr(data, "y") and hasattr(data, "z"):
185199
if np.array_equal(data.x, x) and np.array_equal(data.y, y) and np.array_equal(data.z, z):
186200
return data.name, data.z_name
187-
data_obj = Data3D(x, y, z, name)
201+
data_obj = Data3D(x, y, z, name, decimate=decimate)
188202
self.data.append(data_obj)
189203
return data_obj.name
190204

@@ -202,8 +216,20 @@ def export_data(self):
202216
if hasattr(data, "z"):
203217
export_string += "\\pgfplotstableread{\n"
204218
export_string += "x y z\n"
205-
for x, y, z in zip(data.x, data.y, data.z):
206-
export_string += f"{treat_data(x)} {treat_data(y)} {treat_data(z)}\n"
219+
n = len(data.x)
220+
if getattr(data, 'decimate', None) is None:
221+
indices = range(n)
222+
else:
223+
d = data.decimate
224+
if d <= 1:
225+
indices = range(n)
226+
else:
227+
# always include first and last
228+
indices = list(range(0, n, d))
229+
if indices[-1] != n-1:
230+
indices.append(n-1)
231+
for i in indices:
232+
export_string += f"{treat_data(data.x[i])} {treat_data(data.y[i])} {treat_data(data.z[i])}\n"
207233
export_string += f"}}{{\\{data.name}}}\n"
208234

209235
# 2D
@@ -216,8 +242,19 @@ def export_data(self):
216242
else:
217243
header += " y"
218244
export_string += header + "\n"
219-
for i, x in enumerate(data.x):
220-
row = [treat_data(x)]
245+
n = len(data.x)
246+
if getattr(data, 'decimate', None) is None:
247+
indices = range(n)
248+
else:
249+
d = data.decimate
250+
if d <= 1:
251+
indices = range(n)
252+
else:
253+
indices = list(range(0, n, d))
254+
if indices[-1] != n-1:
255+
indices.append(n-1)
256+
for i in indices:
257+
row = [treat_data(data.x[i])]
221258
for y_col in data.y_data:
222259
row.append(treat_data(y_col[i]))
223260
export_string += " ".join(row) + "\n"

0 commit comments

Comments
 (0)