Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(issue 900): implemented Range.add_plot() and add_table() #926

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/source/changes/version_0_33.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ Miscellaneous improvements

* implemented :py:obj:`Axis.min()` and :py:obj:`Axis.max()` methods (closes :issue:`874`).

* implemented the :py:obj:`Range.add_plot()` method that allows to create graphs when using
`open_excel()` (closes :issue:`900`).

* implemented the :py:obj:`Range.add_table()` method.

Fixes
^^^^^

Expand Down
17 changes: 15 additions & 2 deletions doc/source/tutorial/tutorial_IO.ipyml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ cells:
sh = wb['population_births_deaths']
# dump the array population in sheet 'population_births_deaths' starting at cell A2
sh['A2'] = population.dump()
# or equivalently
sh['A2'].add_table(population)
# add 'births' in cell A10
sh['A10'] = 'births'
# dump the array births in sheet 'population_births_deaths' starting at cell A11
Expand All @@ -334,6 +336,17 @@ cells:
```


- markdown: |
<div class="alert alert-info">
**Note:** The two syntaxes:
<ul>
<li> sheet['cell'] = array.dump() </li>
<li> sheet['cell'].add_table(array) </li>
</ul>
are equivalent.
</div>


- markdown: |
## Exporting data without headers (Excel)

Expand Down Expand Up @@ -541,7 +554,7 @@ cells:
metadata:
celltoolbar: Edit Metadata
kernelspec:
display_name: Python 3
display_name: Python 3 (ipykernel)
language: python
name: python3
language_info:
Expand All @@ -553,7 +566,7 @@ metadata:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.7.3
version: 3.9.5
livereveal:
autolaunch: false
scroll: true
Expand Down
20 changes: 18 additions & 2 deletions doc/source/tutorial/tutorial_IO.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@
" sh = wb['population_births_deaths']\n",
" # dump the array population in sheet 'population_births_deaths' starting at cell A2\n",
" sh['A2'] = population.dump()\n",
" # or equivalently\n",
" sh['A2'].add_table(population)\n",
" # add 'births' in cell A10\n",
" sh['A10'] = 'births'\n",
" # dump the array births in sheet 'population_births_deaths' starting at cell A11 \n",
Expand All @@ -480,6 +482,20 @@
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" **Note:** The two syntaxes:\n",
" <ul> \n",
" <li> sheet['cell'] = array.dump() </li>\n",
" <li> sheet['cell'].add_table(array) </li>\n",
" </ul>\n",
" are equivalent.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -774,7 +790,7 @@
"metadata": {
"celltoolbar": "Edit Metadata",
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -788,7 +804,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
"version": "3.9.5"
},
"livereveal": {
"autolaunch": false,
Expand Down
46 changes: 44 additions & 2 deletions doc/source/tutorial/tutorial_plotting.ipyml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ cells:
%matplotlib inline


- markdown: |
## Python Plot (matplotlib)


- markdown: |
In a Python script, add the following import on top of the script:

Expand Down Expand Up @@ -145,12 +149,50 @@ cells:
See [pyplot tutorial](https://matplotlib.org/tutorials/introductory/pyplot.html) for a short introduction to `matplotlib.pyplot`.


- markdown: |
## Excel Plot


- markdown: |
It is possible to dump arrays and to make plots in the same time in an Excel workbook:


- markdown: |
```python
# to create a new Excel file, argument overwrite_file must be set to True
with open_excel('workbook_with_plots.xlsx', overwrite_file=True) as wb:
# ---- dump data
# add a new sheet 'BFG' and dump the data for Belgium in it
wb['BFG'] = population['Belgium'].dump()
# store the BFG sheet in a local variable
sh = wb['BFG']
# dump the data for France using the equivalent method add_table()
sh['A18'].add_table(population['France'])
# dump the data for Germany
sh['A35'].add_table(population['Germany'])

# ---- use data to create plots
# basic plot anchored to cell H1 using data for Belgium
sh['H1'].add_plot('A1', title='Belgium population')
# basic plot anchored to cell H18 using data for the France for years 2013 to 2016
sh['H18'].add_plot('A18:E20', title='France population')
# plot with options anchored to cell H35 using data for Germany
sh["H35"].add_plot("A35", title="Germany population", width=500, height=300,
xticks_spacing=2, min_y=40_000_000, max_y=41_500_000)

# actually write data and plots in the Workbook
wb.save()

# the Workbook is automatically closed when getting out the block defined by the with statement
```


# The lines below here may be deleted if you do not need them.
# ---------------------------------------------------------------------------
metadata:
celltoolbar: Edit Metadata
kernelspec:
display_name: Python 3
display_name: Python 3 (ipykernel)
language: python
name: python3
language_info:
Expand All @@ -162,7 +204,7 @@ metadata:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.7.3
version: 3.9.5
livereveal:
autolaunch: false
scroll: true
Expand Down
58 changes: 56 additions & 2 deletions doc/source/tutorial/tutorial_plotting.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python Plot (matplotlib)"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -246,12 +253,59 @@
"\n",
"See [pyplot tutorial](https://matplotlib.org/tutorials/introductory/pyplot.html) for a short introduction to `matplotlib.pyplot`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Excel Plot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is possible to dump arrays and to make plots in the same time in an Excel workbook:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"# to create a new Excel file, argument overwrite_file must be set to True\n",
"with open_excel('workbook_with_plots.xlsx', overwrite_file=True) as wb:\n",
" # ---- dump data\n",
" # add a new sheet 'BFG' and dump the data for Belgium in it \n",
" wb['BFG'] = population['Belgium'].dump()\n",
" # store the BFG sheet in a local variable\n",
" sh = wb['BFG']\n",
" # dump the data for France using the equivalent method add_table()\n",
" sh['A18'].add_table(population['France'])\n",
" # dump the data for Germany\n",
" sh['A35'].add_table(population['Germany'])\n",
" \n",
" # ---- use data to create plots\n",
" # basic plot anchored to cell H1 using data for Belgium\n",
" sh['H1'].add_plot('A1', title='Belgium population')\n",
" # basic plot anchored to cell H18 using data for the France for years 2013 to 2016\n",
" sh['H18'].add_plot('A18:E20', title='France population')\n",
" # plot with options anchored to cell H35 using data for Germany\n",
" sh[\"H35\"].add_plot(\"A35\", title=\"Germany population\", width=500, height=300, \n",
" xticks_spacing=2, min_y=40_000_000, max_y=41_500_000)\n",
" \n",
" # actually write data and plots in the Workbook\n",
" wb.save()\n",
" \n",
"# the Workbook is automatically closed when getting out the block defined by the with statement\n",
"```"
]
}
],
"metadata": {
"celltoolbar": "Edit Metadata",
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -265,7 +319,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
"version": "3.9.5"
},
"livereveal": {
"autolaunch": false,
Expand Down
90 changes: 82 additions & 8 deletions larray/inout/xw_excel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import atexit

from typing import Union, Dict, Callable, Any

import numpy as np
try:
import xlwings as xw
Expand Down Expand Up @@ -618,6 +620,69 @@ def load(self, header=True, convert_float=True, nb_axes=None, index_col=None, fi
else:
return Array(list_data)

def add_table(self, array: Array):
self.xw_range.value = array.dump()

def add_plot(self, data_source: str, width: int=427, height: int=230, title: str=None, template: str=None,
min_y: Union[int, float]=None, max_y: Union[int, float]=None,
xticks_spacing: Union[int, float]=None, customize_func: Callable=None,
customize_kwargs: Dict[str, str]=None) -> Any:
from xlwings.constants import LegendPosition, ChartType, RowCol, AxisType, Constants, Direction
if customize_func is not None and not callable(customize_func):
raise TypeError(f"Expected a function for the argument 'customize_func'. "
f"Got object of type {type(customize_func).__name__} instead.")
if template is not None and not os.path.isfile(template):
raise ValueError(f"Could not find template file {template}")
title = str(title) if title is not None else None
sheet = self.sheet.xw_sheet.api
data_range = sheet.Range(data_source)
top_left_cell = data_range.Cells(1, 1)
# expand if current range is one cell
if data_range.Count == 1:
bottom_left_cell = data_range.End(Direction.xlDown)
bottom_right_cell = bottom_left_cell.End(Direction.xlToRight)
data_range = sheet.Range(top_left_cell, bottom_right_cell)
# horrible hack to make sure that Excel will consider the first column as the xticks
top_left_cell_value = top_left_cell.Value
top_left_cell.Value = ''
# start chart
sheet_charts = sheet.ChartObjects()
range_chart = self.xw_range.api
left, top = range_chart.Left, range_chart.Top
obj = sheet_charts.Add(left, top, width, height)
obj_chart = obj.Chart
obj_chart.SetSourceData(data_range)
obj_chart.ChartType = ChartType.xlLine
# title
if title is not None:
obj_chart.HasTitle = True
obj_chart.ChartTitle.Caption = title
# legend
obj_chart.Legend.Position = LegendPosition.xlLegendPositionBottom
# template
if template is not None:
obj_chart.ApplyChartTemplate(template)
# min - max on Y axis
if min_y is not None:
obj_chart.Axes(AxisType.xlValue).MinimumScale = min_y
if max_y is not None:
obj_chart.Axes(AxisType.xlValue).MaximumScale = max_y
# xticks_spacing
if xticks_spacing is not None:
obj_chart.Axes(AxisType.xlCategory).TickLabelSpacing = xticks_spacing
obj_chart.Axes(AxisType.xlCategory).TickMarkSpacing = xticks_spacing
obj_chart.Axes(AxisType.xlCategory).TickLabelPosition = Constants.xlLow
# user's function (to apply on remaining kwargs)
if customize_func is not None:
customize_func(obj_chart, **customize_kwargs)
# flagflip
nb_xticks = data_range.Rows.Count - 1
nb_series = data_range.Columns.Count - 1
if nb_series > 1 and nb_xticks == 1:
obj_chart.PlotBy = RowCol.xlRows
# see above
top_left_cell.Value = top_left_cell_value
return obj_chart

# XXX: deprecate this function?
def open_excel(filepath=None, overwrite_file=False, visible=None, silent=None, app=None, load_addins=None):
Expand Down Expand Up @@ -744,25 +809,34 @@ def open_excel(filepath=None, overwrite_file=False, visible=None, silent=None, a

Examples
--------
>>> arr = ndtest((3, 3))
>>> arr
a\b b0 b1 b2
a0 0 1 2
a1 3 4 5
a2 6 7 8
>>> arr, arr2 = ndtest((3, 3)), ndtest((2, 2))

create a new Excel file and save an array

>>> # to create a new Excel file, argument overwrite_file must be set to True
>>> with open_excel('excel_file.xlsx', overwrite_file=True) as wb: # doctest: +SKIP
... # create a new sheet 'arr' and dump the array 'arr' starting at cell A1
... wb['arr'] = arr.dump()
...
... # dump array 'arr2' starting at cell F1
... wb['arr']['F1'].add_table(arr2)
...
... # add a plot with left top corner anchored to cell A6 and
... # using data in range A1:D4
... wb['arr']['A6'].add_plot('A1:D4', title='simple graph')
...
... # add a plot with left top corner anchored to cell F6 and
... # using data in range F1:H3 where H3 is deduced automatically
... wb['arr']['F6'].add_plot('F1', title='second simple graph')
...
... save the workbook
... wb.save()

read array from an Excel file

>>> with open_excel('excel_file.xlsx') as wb: # doctest: +SKIP
... arr2 = wb['arr'].load()
>>> arr2 # doctest: +SKIP
... arr3 = wb['arr'].load()
>>> arr3 # doctest: +SKIP
a\b b0 b1 b2
a0 0 1 2
a1 3 4 5
Expand Down
Loading