11import datetime as dt
2- import numpy as np
32import pathlib
4- import pandas as pd
53
6- from functools import partial
7-
8- from .deprecations import deprecated_kwargs
9- from . import utils
10- from copy import deepcopy
114from collections import OrderedDict
125from collections .abc import Iterable
13- from openpyxl import load_workbook
14- from openpyxl .cell .cell import get_column_letter
15- from openpyxl .xml .functions import fromstring , QName
6+ from copy import deepcopy
7+ from functools import partial
8+ from typing import Union , Optional , List , Dict , Tuple , Set
9+
10+ import numpy as np
11+ import pandas as pd
12+
13+ from openpyxl import load_workbook , Workbook
14+ from openpyxl .cell .cell import get_column_letter , Cell
1615from openpyxl .utils import cell
16+ from openpyxl .worksheet .worksheet import Worksheet
17+ from openpyxl .xml .functions import fromstring , QName
1718
1819from styleframe .container import Container
1920from styleframe .series import Series
2021from styleframe .styler import Styler , ColorScaleConditionalFormatRule
22+ from . import utils
2123
2224try :
2325 pd_timestamp = pd .Timestamp
@@ -34,17 +36,23 @@ class StyleFrame:
3436 a list of dictionaries or another StyleFrame.
3537 :param styler_obj: Will be used as the default style of all cells.
3638 :type styler_obj: :class:`.Styler`
39+ :param columns: Names of columns to use. Only applicable if ``obj`` is :class:`numpy.ndarray`
40+ :type columns: None or list[str]
3741 """
38- P_FACTOR = 1.3
39- A_FACTOR = 13
42+ P_FACTOR : Union [ int , float ] = 1.3
43+ A_FACTOR : Union [ int , float ] = 13
4044
41- def __init__ (self , obj , styler_obj = None ):
45+ def __init__ (self , obj , styler_obj : Optional [ Styler ] = None , columns : Optional [ List [ str ]] = None ):
4246 from_another_styleframe = False
4347 from_pandas_dataframe = False
4448 if styler_obj and not isinstance (styler_obj , Styler ):
4549 raise TypeError ('styler_obj must be {}, got {} instead.' .format (Styler .__name__ , type (styler_obj ).__name__ ))
46- if isinstance (obj , pd .DataFrame ):
50+ if isinstance (obj , ( pd .DataFrame , np . ndarray ) ):
4751 from_pandas_dataframe = True
52+ if isinstance (obj , np .ndarray ):
53+ obj = pd .DataFrame (obj )
54+ if columns :
55+ obj = obj .rename (columns = dict (zip (obj .columns , columns )))
4856 if obj .empty :
4957 self .data_df = deepcopy (obj )
5058 else :
@@ -69,7 +77,7 @@ def __init__(self, obj, styler_obj=None):
6977 self ._columns_width = obj ._columns_width if from_another_styleframe else OrderedDict ()
7078 self ._rows_height = obj ._rows_height if from_another_styleframe else OrderedDict ()
7179 self ._has_custom_headers_style = obj ._has_custom_headers_style if from_another_styleframe else False
72- self ._cond_formatting = []
80+ self ._cond_formatting : List [ ColorScaleConditionalFormatRule ] = []
7381 self ._default_style = styler_obj or Styler ()
7482 self ._index_header_style = obj ._index_header_style if from_another_styleframe else self ._default_style
7583
@@ -112,15 +120,15 @@ def __getattr__(self, attr):
112120 raise AttributeError ("'{}' object has no attribute '{}'" .format (type (self ).__name__ , attr ))
113121
114122 @property
115- def columns (self ):
123+ def columns (self ) -> List [ Container ] :
116124 return self .data_df .columns
117125
118126 @columns .setter
119- def columns (self , columns ) :
127+ def columns (self , columns : Iterable ) -> None :
120128 self .data_df .columns = [col if isinstance (col , Container ) else Container (value = col )
121129 for col in columns ]
122130
123- def _get_column_as_letter (self , sheet , column_to_convert , startcol = 0 ) :
131+ def _get_column_as_letter (self , sheet : Worksheet , column_to_convert , startcol : int = 0 ) -> str :
124132 col = column_to_convert .value if isinstance (column_to_convert , Container ) else column_to_convert
125133 if not isinstance (col , (int , str )):
126134 raise TypeError ("column must be an index, column letter or column name" )
@@ -142,19 +150,14 @@ def _get_column_as_letter(self, sheet, column_to_convert, startcol=0):
142150 return column_as_letter
143151
144152 @classmethod
145- def read_excel (cls , path , sheet_name = 0 , read_style = False , use_openpyxl_styles = False ,
146- read_comments = False , ** kwargs ):
153+ def read_excel (cls , path : str , sheet_name : Union [ str , int ] = 0 , read_style : bool = False ,
154+ use_openpyxl_styles : bool = False , read_comments : bool = False , ** kwargs ) -> 'StyleFrame' :
147155 """
148156 Creates a StyleFrame object from an existing Excel.
149157
150158 .. note:: :meth:`read_excel` also accepts all arguments that :func:`pandas.read_excel` accepts as kwargs.
151159
152160 :param str path: The path to the Excel file to read.
153- :param sheetname:
154- .. deprecated:: 1.6
155- Use ``sheet_name`` instead.
156- .. versionchanged:: 4.0
157- Removed
158161 :param sheet_name: The sheet name to read. If an integer is provided then it be used as a zero-based
159162 sheet index. Default is 0.
160163 :type sheet_name: str or int
@@ -175,7 +178,7 @@ def read_excel(cls, path, sheet_name=0, read_style=False, use_openpyxl_styles=Fa
175178 :rtype: :class:`StyleFrame`
176179 """
177180
178- def _get_scheme_colors_from_excel (wb ) :
181+ def _get_scheme_colors_from_excel (wb : Workbook ) -> List [ str ] :
179182 xlmns = 'http://schemas.openxmlformats.org/drawingml/2006/main'
180183 if wb .loaded_theme is None :
181184 return []
@@ -192,7 +195,7 @@ def _get_scheme_colors_from_excel(wb):
192195 colors .append (accent .attrib ['val' ])
193196 return colors
194197
195- def _get_style_object (sheet , theme_colors , row , column ) :
198+ def _get_style_object (sheet : Worksheet , theme_colors : List [ str ] , row : int , column : int ) -> Union [ Cell , Styler ] :
196199 cell = sheet .cell (row = row , column = column )
197200 if use_openpyxl_styles :
198201 return cell
@@ -250,7 +253,7 @@ def _read_style():
250253 return sf
251254
252255 @classmethod
253- def read_excel_as_template (cls , path , df , use_df_boundaries = False , ** kwargs ):
256+ def read_excel_as_template (cls , path : str , df : pd . DataFrame , use_df_boundaries : bool = False , ** kwargs ) -> 'StyleFrame' :
254257 """
255258 .. versionadded:: 3.0.1
256259
@@ -332,9 +335,11 @@ def row_indexes(self):
332335
333336 return tuple (range (1 , len (self ) + 2 ))
334337
335- def to_excel (self , excel_writer = 'output.xlsx' , sheet_name = 'Sheet1' ,
336- allow_protection = False , right_to_left = False , columns_to_hide = None , row_to_add_filters = None ,
337- columns_and_rows_to_freeze = None , best_fit = None , ** kwargs ):
338+ def to_excel (self , excel_writer : Union [str , pd .ExcelWriter , pathlib .Path ] = 'output.xlsx' ,
339+ sheet_name : str = 'Sheet1' , allow_protection : bool = False , right_to_left : bool = False ,
340+ columns_to_hide : Union [None , str , list , tuple , set ] = None , row_to_add_filters : Optional [int ] = None ,
341+ columns_and_rows_to_freeze : Optional [str ] = None , best_fit : Union [None , str , list , tuple , set ] = None ,
342+ ** kwargs ) -> pd .ExcelWriter :
338343 """Saves the dataframe to excel and applies the styles.
339344
340345 .. note:: :meth:`to_excel` also accepts all arguments that :meth:`pandas.DataFrame.to_excel` accepts as kwargs.
@@ -369,11 +374,14 @@ def to_excel(self, excel_writer='output.xlsx', sheet_name='Sheet1',
369374 calling ``StyleFrame.to_excel`` by directly modifying ``StyleFrame.A_FACTOR`` and ``StyleFrame.P_FACTOR``
370375
371376 :type best_fit: None or str or list or tuple or set
372- :return: self
373- :rtype: :class:`StyleFrame`
377+ :rtype: :class:`pandas.ExcelWriter`
374378
375379 """
376380
381+ if isinstance (excel_writer , pd .ExcelWriter ):
382+ if excel_writer .engine != 'openpyxl' :
383+ raise TypeError ('styleframe supports only openpyxl, attempted to use {}' .format (excel_writer .engine ))
384+
377385 # dealing with needed pandas.to_excel defaults
378386 header = kwargs .pop ('header' , True )
379387 index = kwargs .pop ('index' , False )
@@ -393,7 +401,7 @@ def get_values(x):
393401 except TypeError :
394402 return x
395403
396- def within_sheet_boundaries (row = 1 , column = 'A' ):
404+ def within_sheet_boundaries (row : Union [ int , str ] = 1 , column : str = 'A' ):
397405 return (1 <= int (row ) <= sheet .max_row
398406 and
399407 1 <= cell .column_index_from_string (column ) <= sheet .max_column )
@@ -575,8 +583,14 @@ def get_range_of_cells(row_index=None, columns=None):
575583
576584 return excel_writer
577585
578- def apply_style_by_indexes (self , indexes_to_style , styler_obj , cols_to_style = None , height = None ,
579- complement_style = None , complement_height = None , overwrite_default_style = True ):
586+ def apply_style_by_indexes (self ,
587+ indexes_to_style : Union [list , tuple , int , Container ],
588+ styler_obj : Styler ,
589+ cols_to_style : Optional [Union [str , Union [List [str ], Tuple [str ], Set [str ]]]] = None ,
590+ height : Optional [Union [int , float ]] = None ,
591+ complement_style : Optional [Styler ] = None ,
592+ complement_height : Optional [Union [int , float ]] = None ,
593+ overwrite_default_style : bool = True ):
580594 """
581595 Applies a certain style to the provided indexes in the dataframe in the provided columns
582596
@@ -585,7 +599,7 @@ def apply_style_by_indexes(self, indexes_to_style, styler_obj, cols_to_style=Non
585599
586600 ::
587601
588- sf[sf['some_col'] = 20]
602+ sf[sf['some_col'] == 20]
589603
590604 :type indexes_to_style: list or tuple or int or Container
591605 :param styler_obj: `Styler` object that contains the style that will be applied to indexes in `indexes_to_style`
@@ -648,8 +662,13 @@ def apply_style_by_indexes(self, indexes_to_style, styler_obj, cols_to_style=Non
648662
649663 return self
650664
651- def apply_column_style (self , cols_to_style , styler_obj , style_header = False , use_default_formats = True , width = None ,
652- overwrite_default_style = True ):
665+ def apply_column_style (self ,
666+ cols_to_style : Union [str , List [str ], Tuple [str ], Set [str ]],
667+ styler_obj : Styler ,
668+ style_header : bool = False ,
669+ use_default_formats : bool = True ,
670+ width : Optional [Union [int , float ]] = None ,
671+ overwrite_default_style : bool = True ):
653672 """Apply style to a whole column
654673
655674 :param cols_to_style: The column names to style.
@@ -700,7 +719,10 @@ def apply_column_style(self, cols_to_style, styler_obj, style_header=False, use_
700719
701720 return self
702721
703- def apply_headers_style (self , styler_obj , style_index_header = True , cols_to_style = None ):
722+ def apply_headers_style (self ,
723+ styler_obj : Styler ,
724+ style_index_header : bool = True ,
725+ cols_to_style : Optional [Union [str , List [str ], Tuple [str ], Set [str ]]] = None ):
704726 """Apply style to the headers only
705727
706728 :param styler_obj: The style to apply
@@ -737,7 +759,8 @@ def apply_headers_style(self, styler_obj, style_index_header=True, cols_to_style
737759 self ._has_custom_headers_style = True
738760 return self
739761
740- def set_column_width (self , columns , width ):
762+ def set_column_width (self , columns : Union [str , Union [str , List [str ], Tuple [str ], List [int ], Tuple [int ]]],
763+ width : Union [int , float ]) -> 'StyleFrame' :
741764 """Set the width of the given columns
742765
743766 :param columns: Column name(s) or index(es).
@@ -765,7 +788,7 @@ def set_column_width(self, columns, width):
765788
766789 return self
767790
768- def set_column_width_dict (self , col_width_dict ) :
791+ def set_column_width_dict (self , col_width_dict : Dict [ str , Union [ int , float ]]) -> 'StyleFrame' :
769792 """
770793 :param col_width_dict: A dictionary from column names to width.
771794 :type col_width_dict: dict[str, int or float]
@@ -780,7 +803,7 @@ def set_column_width_dict(self, col_width_dict):
780803
781804 return self
782805
783- def set_row_height (self , rows , height ) :
806+ def set_row_height (self , rows : Union [ int , List [ int ], Tuple [ int ], Set [ int ]], height : Union [ int , float ]) -> 'StyleFrame' :
784807 """ Set the height of the given rows
785808
786809 :param rows: Row(s) index.
@@ -810,7 +833,7 @@ def set_row_height(self, rows, height):
810833
811834 return self
812835
813- def set_row_height_dict (self , row_height_dict ) :
836+ def set_row_height_dict (self , row_height_dict : Dict [ int , Union [ int , float ]]) -> 'StyleFrame' :
814837 """
815838 :param row_height_dict: A dictionary from row indexes to height.
816839 :type row_height_dict: dict[int, int or float]
@@ -848,16 +871,16 @@ def rename(self, columns=None, inplace=False):
848871 if old_col_name in sf ._columns_width })
849872 return sf
850873
851- def style_alternate_rows (self , styles , ** kwargs ):
874+ def style_alternate_rows (self , styles : Union [ List [ Styler ], Tuple [ Styler ]], ** kwargs ) -> 'StyleFrame' :
852875 """
853876 .. versionadded:: 1.2
854877
855878 Applies the provided styles to rows in an alternating manner.
856879
857880 .. note:: :meth:`style_alternate_rows` also accepts all arguments that :meth:`apply_style_by_indexes` accepts as kwargs.
858881
859- :param styles: List, tuple or set of :class:`.Styler` objects to be applied to rows in an alternating manner
860- :type styles: list[:class:`.Styler`] or tuple[:class:`.Styler`] or set[:class:`.Styler`]
882+ :param styles: List or tuple of :class:`.Styler` objects to be applied to rows in an alternating manner
883+ :type styles: list[:class:`.Styler`] or tuple[:class:`.Styler`]
861884 :return: self
862885 :rtype: :class:`StyleFrame`
863886
@@ -869,8 +892,17 @@ def style_alternate_rows(self, styles, **kwargs):
869892 self .apply_style_by_indexes (indexes , styles [i ], ** kwargs )
870893 return self
871894
872- def add_color_scale_conditional_formatting (self , start_type , start_value , start_color , end_type , end_value , end_color ,
873- mid_type = None , mid_value = None , mid_color = None , columns_range = None ):
895+ def add_color_scale_conditional_formatting (self ,
896+ start_type : str ,
897+ start_value : Union [int , float ],
898+ start_color : str ,
899+ end_type : str ,
900+ end_value : Union [int , float ],
901+ end_color : str ,
902+ mid_type : Optional [str ] = None ,
903+ mid_value : Optional [Union [int , float ]] = None ,
904+ mid_color : Optional [str ] = None ,
905+ columns_range = None ):
874906 """
875907 :param start_type: The type for the minimum bound
876908 :type start_type: str: one of :class:`.utils.conditional_formatting_types` or any other type Excel supports
0 commit comments