5
\$\begingroup\$

Annother little excercise with openpyxl:

Say you have a Excel file like this:

enter image description here

The Goal is to make make it look like this:

enter image description here

My code to acomplish this:

inversion.py

"""
Inverts the content of table row and columns
"""

from copy import copy
import openpyxl
from openpyxl.utils import get_column_letter

def save_workbook_excel_file(workbook, filename: str):
    """Tries to save created data to excel file"""
    try:
        workbook.save(filename)
    except PermissionError:
        print("Error: No permission to save file.")


def invert_row_column(filename: str):
    """
    Main loop to invert rows and column
    """
    workbook = openpyxl.load_workbook(filename)
    sheet_names = workbook.sheetnames
    sheet = workbook[sheet_names[0]]
    workbook.create_sheet(index=0, title='tmp_sheet')
    tmp_sheet = workbook['tmp_sheet']

    data = []
    for row in sheet:
        cells = []
        for cell in row:
            cells.append(cell)
        data.append(cells)

    for x in range(0, len(data)):
        for y in range(0, len(data[x])):
            column = get_column_letter(x + 1)
            row = str(y + 1)
            tmp_sheet[column + row] = copy(data[x][y].value)

    sheet_name = sheet.title
    del workbook[sheet_name]
    tmp_sheet.title = sheet_name
    save_workbook_excel_file(workbook, 'updated_' + filename)


invert_row_column("test.xlsx")

I wonder how this can be improved? Can the naming be better? Is there a better / or shorter solution?

\$\endgroup\$
  • 1
    \$\begingroup\$ a small tip: don't overwrite the original file, but write it to a different filename, or rename the original file \$\endgroup\$ – Maarten Fabré Dec 18 '18 at 16:51
3
\$\begingroup\$

Disclaimer: I'm not familiar with openpyxl. I hope this review won't be nonsense. Do tell me!

The posted code copies the content of the first sheet into data, writes inverted (transposed?) content into a new sheet tmp_sheet, copies attributes of the original sheet to tmp_sheet and finally deletes the original sheet.

What I don't get is why not update the original sheet directly? You could loop over coordinates of the cells below the diagonal of the sheet, compute the coordinates of the cell to swap with, use a suitable temporary storage for swapping single values. The diagonal can be left alone, they don't need to be swapped with anything.

This approach would have the advantages that if there are multiple sheets in the file, the content of the first sheet stays on the first sheet, and you don't need to worry about copying properties of the sheet such as the title.

\$\endgroup\$
6
\$\begingroup\$

Another way to solve this, without using openpyxl and thereby slightly defeating the purpose of learning more about that, would be to use pandas, where this is quite short:

import pandas as pd

# This would load the first sheet by default, but we need the name to save it again
# df = pd.read_excel(filename)  

sheets = pd.read_excel(file_name, sheet_name=None)  # all sheets
sheet_name, df = next(iter(sheets.items()))         # first sheet
df = df.T                                           # transpose
df.to_excel(file_name, sheet_name)                  # write back

This uses pandas.read_excel to read the file and pandas.DataFrame.to_excel to write it back. You need to have the xlrd module installed for this to work.

Feel free to wrap it in functions again if needed.

This should be faster than the manual iteration in Python, since the transpose should happen at C speed.

\$\endgroup\$
  • \$\begingroup\$ were does the inversion happen? in df.T.to_excel(file_name, sheet_name) ? \$\endgroup\$ – Sandro4912 Dec 18 '18 at 18:42
  • \$\begingroup\$ @Sandro4912 The .T stands for transpose. \$\endgroup\$ – Graipher Dec 18 '18 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.