DeepSpace2 / StyleFrame

A library that wraps pandas and openpyxl and allows easy styling of dataframes in excel
MIT License
369 stars 54 forks source link

AttributeError: 'Series' object has no attribute 'style' #130

Open buhtz opened 2 years ago

buhtz commented 2 years ago

While creating a minimal working example to illustrate a problem I run into this error:

Traceback (most recent call last):
  File "C:\Users\buhtzch\Desktop\x.py", line 25, in <module>
    sf.to_excel(file_path).save()
  File "C:\Users\buhtzch\AppData\Roaming\Python\Python39\site-packages\styleframe\style_frame.py", line 503, in to_excel
    data_df_style = self.data_df.at[index, column].style
  File "C:\Users\buhtzch\AppData\Roaming\Python\Python39\site-packages\pandas\core\generic.py", line 5478, in __getattr__
    return object.__getattribute__(self, name)
AttributeError: 'Series' object has no attribute 'style'

And I don't understand the root of the problem. Maybe I did something wrong or this is a bug or edgy use case?

Version info

Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
pandas 1.3.0
openpyxl 3.0.9
StyleFrame 4.1

This is the code to reproduce.

#!/usr/bin/env python3
import os
import sys
import pathlib
import pandas
import styleframe
import styleframe.utils

print(styleframe._versions_)

df = pandas.DataFrame(
    data={
        'idx1': list('AABB'),
        'column with long caption': [1234, 345, 33123, 2],
        }
    )
df = df.set_index('idx1')
print(df)

file_path = pathlib.Path.cwd() / 'test.xlsx'

default_style = styleframe.Styler(font_size=14)
sf = styleframe.StyleFrame(df, styler_obj=default_style)

sf.to_excel(file_path).save()
os.system(str(file_path))

EDIT: The exception is not raised when I out-comment the line df = df.set_index('idx1').

buhtz commented 2 years ago

I realized how much Issues I opened today. My apologize. :) I am really willing to support you as much I can.

In case of that Issue the "problem" is located at this line https://github.com/DeepSpace2/StyleFrame/blob/c8a11172d84d4af6eab7c75e7563a00926267765/styleframe/style_frame.py#L503

The data frame in my example has an un-unique index. Pandas does support un-unique indexes. But your current code assumes that the index is unique.

The whole code of .to_excel() is to long (~200 lines). So I won't dive into it because I am scared to break something. But my suggestion would be to not iterate over the index but over the row by count via iloc[]. This makes you independent from the uniqueness of the index.

Beside that problem the to_excel() should be refactored. In the current state it is nearly impossible to understand that code by external contributors. I would give this a try if you aggree.

EDIT: The files in tests directory are without any documentation. I would like to add a test case for that problem but don't know where. I would suggest to minimally add a docstring for each TestCase derived class to make it a bit clearer for new contributors.

DeepSpace2 commented 2 years ago

Feel free to open as many issues as you can find :) I appreciate the willingness and any contribution is welcome.

gribna commented 2 years ago

This exact error also occurs for me when iterating over a list of dataframes.

The code is:

with pd.ExcelWriter(f'_{file}', engine='xlsxwriter') as writer:
    row = 0

    for dataframe, data in zip(tables, metadata):            
        data.to_excel(writer, startrow = row , startcol = 0, index = False, header = False)   
        row = row + data.shape[0]

        sf = StyleFrame(dataframe)
        format1 = Styler(bg_color = '#DCE6F1')
        sf.apply_style_by_indexes(indexes_to_style=sf.index, cols_to_style=[sf.columns[0], sf.columns[2], sf.columns[4]], styler_obj=format1)

        sf.to_excel(writer, startrow = row , startcol = 0, index = False)

        row = row + dataframe.shape[0] + spaces + 1

The subject dataframes in tables do have unique indices.

DeepSpace2 commented 2 years ago

Hi. I can't test right now, but I guess this is due to the usage of 'xlsxwriter' as an engine. Try to use 'openpyxl' which is the only engine that styleframe supports.

I don't see how the iteration in itself can lead to this error.

buhtz commented 2 years ago

Dear @gribna I also consider that your problem is separate from mine. I would recommend to open a new Issue with your example code. But improve your code and make it work out of the box including sample data. Your code is not clear for us. This would help us to analyze.

I use the "default engine" (openpyxl) set by StyleFrame but the problem occurs.

chunkhai96 commented 1 year ago

I got this issue too when exporting to excel ...

C:\anaconda3\envs\predict-comp-pipeline\lib\site-packages\styleframe\style_frame.py in to_excel(self, excel_writer, sheet_name, allow_protection, right_to_left, columns_to_hide, row_to_add_filters, columns_and_rows_to_freeze, best_fit, **kwargs)
    493             for row_index, index in enumerate(self.data_df.index):
    494                 current_cell = sheet.cell(row=row_index + startrow + (2 if header else 1), column=col_index + startcol + 1)
--> 495                 data_df_style = self.data_df.at[index, column].style
    496                 try:
    497                     if '=HYPERLINK' in str(current_cell.value):

~\AppData\Roaming\Python\Python38\site-packages\pandas\core\generic.py in __getattr__(self, name)
   5137             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5138                 return self[name]
-> 5139             return object.__getattribute__(self, name)
   5140 
   5141     def __setattr__(self, name: str, value) -> None:

AttributeError: 'Series' object has no attribute 'style'
odkken commented 1 year ago

Also seeing this, confirmed it is due to a duplicate index