1
1
import datetime as dt
2
- import numpy as np
3
2
import pathlib
4
- import pandas as pd
5
3
6
- from functools import partial
7
-
8
- from .deprecations import deprecated_kwargs
9
- from . import utils
10
- from copy import deepcopy
11
4
from collections import OrderedDict
12
5
from 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
16
15
from openpyxl .utils import cell
16
+ from openpyxl .worksheet .worksheet import Worksheet
17
+ from openpyxl .xml .functions import fromstring , QName
17
18
18
19
from styleframe .container import Container
19
20
from styleframe .series import Series
20
21
from styleframe .styler import Styler , ColorScaleConditionalFormatRule
22
+ from . import utils
21
23
22
24
try :
23
25
pd_timestamp = pd .Timestamp
@@ -34,17 +36,23 @@ class StyleFrame:
34
36
a list of dictionaries or another StyleFrame.
35
37
:param styler_obj: Will be used as the default style of all cells.
36
38
: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]
37
41
"""
38
- P_FACTOR = 1.3
39
- A_FACTOR = 13
42
+ P_FACTOR : Union [ int , float ] = 1.3
43
+ A_FACTOR : Union [ int , float ] = 13
40
44
41
- def __init__ (self , obj , styler_obj = None ):
45
+ def __init__ (self , obj , styler_obj : Optional [ Styler ] = None , columns : Optional [ List [ str ]] = None ):
42
46
from_another_styleframe = False
43
47
from_pandas_dataframe = False
44
48
if styler_obj and not isinstance (styler_obj , Styler ):
45
49
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 ) ):
47
51
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 )))
48
56
if obj .empty :
49
57
self .data_df = deepcopy (obj )
50
58
else :
@@ -69,7 +77,7 @@ def __init__(self, obj, styler_obj=None):
69
77
self ._columns_width = obj ._columns_width if from_another_styleframe else OrderedDict ()
70
78
self ._rows_height = obj ._rows_height if from_another_styleframe else OrderedDict ()
71
79
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 ] = []
73
81
self ._default_style = styler_obj or Styler ()
74
82
self ._index_header_style = obj ._index_header_style if from_another_styleframe else self ._default_style
75
83
@@ -112,15 +120,15 @@ def __getattr__(self, attr):
112
120
raise AttributeError ("'{}' object has no attribute '{}'" .format (type (self ).__name__ , attr ))
113
121
114
122
@property
115
- def columns (self ):
123
+ def columns (self ) -> List [ Container ] :
116
124
return self .data_df .columns
117
125
118
126
@columns .setter
119
- def columns (self , columns ) :
127
+ def columns (self , columns : Iterable ) -> None :
120
128
self .data_df .columns = [col if isinstance (col , Container ) else Container (value = col )
121
129
for col in columns ]
122
130
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 :
124
132
col = column_to_convert .value if isinstance (column_to_convert , Container ) else column_to_convert
125
133
if not isinstance (col , (int , str )):
126
134
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):
142
150
return column_as_letter
143
151
144
152
@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' :
147
155
"""
148
156
Creates a StyleFrame object from an existing Excel.
149
157
150
158
.. note:: :meth:`read_excel` also accepts all arguments that :func:`pandas.read_excel` accepts as kwargs.
151
159
152
160
: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
158
161
:param sheet_name: The sheet name to read. If an integer is provided then it be used as a zero-based
159
162
sheet index. Default is 0.
160
163
: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
175
178
:rtype: :class:`StyleFrame`
176
179
"""
177
180
178
- def _get_scheme_colors_from_excel (wb ) :
181
+ def _get_scheme_colors_from_excel (wb : Workbook ) -> List [ str ] :
179
182
xlmns = 'http://schemas.openxmlformats.org/drawingml/2006/main'
180
183
if wb .loaded_theme is None :
181
184
return []
@@ -192,7 +195,7 @@ def _get_scheme_colors_from_excel(wb):
192
195
colors .append (accent .attrib ['val' ])
193
196
return colors
194
197
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 ] :
196
199
cell = sheet .cell (row = row , column = column )
197
200
if use_openpyxl_styles :
198
201
return cell
@@ -250,7 +253,7 @@ def _read_style():
250
253
return sf
251
254
252
255
@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' :
254
257
"""
255
258
.. versionadded:: 3.0.1
256
259
@@ -332,9 +335,11 @@ def row_indexes(self):
332
335
333
336
return tuple (range (1 , len (self ) + 2 ))
334
337
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 :
338
343
"""Saves the dataframe to excel and applies the styles.
339
344
340
345
.. 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',
369
374
calling ``StyleFrame.to_excel`` by directly modifying ``StyleFrame.A_FACTOR`` and ``StyleFrame.P_FACTOR``
370
375
371
376
:type best_fit: None or str or list or tuple or set
372
- :return: self
373
- :rtype: :class:`StyleFrame`
377
+ :rtype: :class:`pandas.ExcelWriter`
374
378
375
379
"""
376
380
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
+
377
385
# dealing with needed pandas.to_excel defaults
378
386
header = kwargs .pop ('header' , True )
379
387
index = kwargs .pop ('index' , False )
@@ -393,7 +401,7 @@ def get_values(x):
393
401
except TypeError :
394
402
return x
395
403
396
- def within_sheet_boundaries (row = 1 , column = 'A' ):
404
+ def within_sheet_boundaries (row : Union [ int , str ] = 1 , column : str = 'A' ):
397
405
return (1 <= int (row ) <= sheet .max_row
398
406
and
399
407
1 <= cell .column_index_from_string (column ) <= sheet .max_column )
@@ -575,8 +583,14 @@ def get_range_of_cells(row_index=None, columns=None):
575
583
576
584
return excel_writer
577
585
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 ):
580
594
"""
581
595
Applies a certain style to the provided indexes in the dataframe in the provided columns
582
596
@@ -585,7 +599,7 @@ def apply_style_by_indexes(self, indexes_to_style, styler_obj, cols_to_style=Non
585
599
586
600
::
587
601
588
- sf[sf['some_col'] = 20]
602
+ sf[sf['some_col'] == 20]
589
603
590
604
:type indexes_to_style: list or tuple or int or Container
591
605
: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
648
662
649
663
return self
650
664
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 ):
653
672
"""Apply style to a whole column
654
673
655
674
: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_
700
719
701
720
return self
702
721
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 ):
704
726
"""Apply style to the headers only
705
727
706
728
: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
737
759
self ._has_custom_headers_style = True
738
760
return self
739
761
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' :
741
764
"""Set the width of the given columns
742
765
743
766
:param columns: Column name(s) or index(es).
@@ -765,7 +788,7 @@ def set_column_width(self, columns, width):
765
788
766
789
return self
767
790
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' :
769
792
"""
770
793
:param col_width_dict: A dictionary from column names to width.
771
794
:type col_width_dict: dict[str, int or float]
@@ -780,7 +803,7 @@ def set_column_width_dict(self, col_width_dict):
780
803
781
804
return self
782
805
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' :
784
807
""" Set the height of the given rows
785
808
786
809
:param rows: Row(s) index.
@@ -810,7 +833,7 @@ def set_row_height(self, rows, height):
810
833
811
834
return self
812
835
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' :
814
837
"""
815
838
:param row_height_dict: A dictionary from row indexes to height.
816
839
:type row_height_dict: dict[int, int or float]
@@ -848,16 +871,16 @@ def rename(self, columns=None, inplace=False):
848
871
if old_col_name in sf ._columns_width })
849
872
return sf
850
873
851
- def style_alternate_rows (self , styles , ** kwargs ):
874
+ def style_alternate_rows (self , styles : Union [ List [ Styler ], Tuple [ Styler ]], ** kwargs ) -> 'StyleFrame' :
852
875
"""
853
876
.. versionadded:: 1.2
854
877
855
878
Applies the provided styles to rows in an alternating manner.
856
879
857
880
.. note:: :meth:`style_alternate_rows` also accepts all arguments that :meth:`apply_style_by_indexes` accepts as kwargs.
858
881
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`]
861
884
:return: self
862
885
:rtype: :class:`StyleFrame`
863
886
@@ -869,8 +892,17 @@ def style_alternate_rows(self, styles, **kwargs):
869
892
self .apply_style_by_indexes (indexes , styles [i ], ** kwargs )
870
893
return self
871
894
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 ):
874
906
"""
875
907
:param start_type: The type for the minimum bound
876
908
:type start_type: str: one of :class:`.utils.conditional_formatting_types` or any other type Excel supports
0 commit comments