Files
OpenBB/bots/common/plot_tbls.py
teh_coderer a36da387e2 fixes/tbl-columns-width/refactor (#1527)
* few changes

* import fix

* missed removing test config

* column order fix

* import fix

* removed df2img add new deps

* req

* update readme

* add dep for image export, fix

* get rid of yahoo-fin, add image export lib

* import issues in some machines fix

* images dir fix

* init fix

* fix?

* test

* test

* Fixed text handling

Co-authored-by: Colin Delahunty <72827203+colin99d@users.noreply.github.com>
Co-authored-by: DjangoServer <colin99delahunty@gmail.com>
2022-03-21 11:54:19 -04:00

167 lines
5.9 KiB
Python

from math import floor
from typing import List, Optional, Tuple, Union
import pandas as pd
import plotly
# pylint: disable=R0913
def plot_df(
df: Union[pd.Series, pd.DataFrame],
print_index: bool = True,
title: Optional[dict] = None,
tbl_header_visible: bool = True,
tbl_header: Optional[dict] = None,
tbl_cells: Optional[dict] = None,
row_fill_color: Optional[Tuple[str, str]] = None,
col_width: Optional[Union[int, float, List[Union[int, float]]]] = None,
fig_size: Optional[Tuple[int, int]] = None,
**layout_kwargs,
) -> plotly.graph_objects.Figure:
"""Plots a pd.Series or pd.DataFrame.
Parameters
----------
df : Union[pd.Series, pd.DataFrame]
Series or dataframe to be plotted.
print_index : bool, default True
If `True`, prints the dataframe's index. `df.index.name` will become the index
column header.
title : dict, default None
A dict possibly containing `plotly` key/value pairs:
https://plotly.com/python/reference/layout/#layout-title
More relevant key/value pairs:
- font_color : color
- font_family : str
- font_size : number greater than or equal to 1
- text : str
- x : number between or equal to 0 and 1, default 0.5
Sets the x position with respect to `xref` in normalized coordinates
from "0" (left) to "1" (right).
- xanchor : enumerated, one of ("auto", "left", "center", "right"),
default "auto"
Sets the title's horizontal alignment with respect to its x position.
"left" means that the title starts at x, "right" means that the title ends
at x and "center" means that the title's center is at x. "auto" divides
`xref` by three and calculates the `xanchor` value automatically based on
the value of `x`.
tbl_header_visible : bool, default True,
If `False`, table header will be invisible. Takes precedence over `tbl_header`
argument.
tbl_header, tbl_cells : dict, default None
A dict possibly containing `plotly` key/value pairs:
https://plotly.com/python/reference/table/#table-header
https://plotly.com/python/reference/table/#table-cells
More relevant key/value pairs:
- align : enumerated or array of enumerateds,
one of ("left", "center", "right"), default "center"
- fill_color : color, default "white"
- font_color : color or array of colors
- font_family : str or array of str
- font_size : number or array of numbers greater than or equal to 1
- height : number, default 28
- line_width : number or array of numbers, default 1
row_fill_color : Tuple[str, str], default None
Tuple of colors that will be used to alternate row colors. Takes precedence
over `tbl_cells["fill_color"]`.
col_width : number or array of numbers, default None
The width of columns expressed as a ratio. Columns fill the available width
in proportion of their specified column widths.
fig_size : Tuple[int, int], default None
Tuple specifying the `width` and `height` of the figure.
**layout_kwargs
Plotly accepts a large number of layout-related keyword arguments.
A detailed descriptions is available at
https://plotly.com/python-api-reference/generated/plotly.graph_objects.Layout.html.
Returns
-------
plotly.graph_objects.Figure
Returns a figure object.
"""
def _alternate_row_colors() -> Optional[List[str]]:
color_list = None
# alternate row colors
row_count = len(df)
if row_fill_color is not None:
# determine how many rows in `df` and then create a list with alternating
# row colors
row_odd_count = floor(row_count / 2) + row_count % 2
row_even_count = floor(row_count / 2)
odd_list = [row_fill_color[0]] * row_odd_count
even_list = [row_fill_color[1]] * row_even_count
color_list = [x for y in zip(odd_list, even_list) for x in y]
if row_odd_count > row_even_count:
color_list.append(row_fill_color[0])
return color_list
def _tbl_values():
if print_index:
header_values = [
"<b>" + x + "<b>"
for x in [
df.index.name if df.index.name is not None else "",
*df.columns,
]
]
cell_values = [df.index, *[df[col] for col in df]]
else:
header_values = ["<b>" + x + "<b>" for x in df.columns.to_list()]
cell_values = [df[col] for col in df]
return header_values, cell_values
row_color_list = _alternate_row_colors()
header_vals, cell_vals = _tbl_values()
if not tbl_header:
tbl_header = dict()
tbl_header.update(values=header_vals)
if not tbl_header_visible:
tbl_header.update(
fill_color="white", font_color="white", line_color="white", height=1
)
if not tbl_cells:
tbl_cells = dict()
tbl_cells.update(
values=cell_vals,
fill_color=[row_color_list] * len(df)
if row_color_list
else tbl_cells.get("fill_color"),
)
fig = plotly.graph_objs.Figure(
data=[plotly.graph_objs.Table(header=tbl_header, cells=tbl_cells)]
)
fig.data[0]["columnwidth"] = col_width if col_width else None
if not title:
title = dict()
title.update(
x=0.01 if title.get("x") is None else title.get("x"),
xanchor="left" if title.get("xanchor") is None else title.get("xanchor"),
)
fig.update_layout(
title=title,
margin=dict(autoexpand=False, b=5, l=5, r=5, t=40 if title.get("text") else 5),
width=fig_size[0] if fig_size else None,
height=fig_size[1] if fig_size else None,
autosize=False if col_width else None,
**layout_kwargs,
)
return fig