├── .gitattributes ├── .gitignore ├── General ├── excel_clipboard_to_dataframe.ipynb └── test.xlsx ├── Pivot_Tables_and_Table ├── Images │ ├── long_data.JPG │ ├── long_data_pivot_table.JPG │ └── pivot_table_fields.JPG ├── README.md ├── create_pivot_table_with_win32com.ipynb ├── pivot_table_repo_image.png ├── pivot_tables.ipynb ├── pivot_tables.py └── pivot_tables.xlsx ├── README.md ├── _config.yml └── excel_automation_repo_image.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | */.ipynb_checkpoints/* 3 | Pivot_Tables_and_Table/test.xlsx 4 | -------------------------------------------------------------------------------- /General/excel_clipboard_to_dataframe.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "3a813c17", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import win32com.client as win32\n", 11 | "from pathlib import Path\n", 12 | "import sys\n", 13 | "import pandas as pd # only used for synthetic data\n", 14 | "from pywintypes import com_error\n", 15 | "\n", 16 | "\n", 17 | "win32c = win32.constants" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "id": "5923727b", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "# create excel object\n", 28 | "excel = win32.gencache.EnsureDispatch('Excel.Application')\n", 29 | "\n", 30 | "# excel can be visible or not\n", 31 | "excel.Visible = True # False\n", 32 | "\n", 33 | "filename = Path.cwd() / 'test.xlsx'\n", 34 | "\n", 35 | "# try except for file / path\n", 36 | "try:\n", 37 | " wb = excel.Workbooks.Open(filename)\n", 38 | "except com_error as e:\n", 39 | " print(e.excepinfo)\n", 40 | " if e.excepinfo[5] == -2146827284:\n", 41 | " print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')\n", 42 | " else:\n", 43 | " raise e\n", 44 | " sys.exit(1)\n", 45 | " \n", 46 | "# set worksheet\n", 47 | "ws1 = wb.Sheets('Sheet1')" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "id": "882f124c", 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "used = ws1.UsedRange" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "id": "704be317", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "used.Row" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "id": "10d4fe94", 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "used.Rows.Count" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "id": "fa38f237", 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "used.Column" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "id": "4758f45a", 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "used.Columns.Count" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "id": "04c2e246", 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "# selects the entire worksheet\n", 108 | "ws1.Cells.Select()" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "id": "ea731153", 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "# selects the specifc range\n", 119 | "ws1.Range('C3:D17').Select()" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "id": "79ef99e9", 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "# this copies the selected region\n", 130 | "excel.Selection.Copy()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "id": "b719bc77", 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "# selects a new cell\n", 141 | "ws1.Range(\"G3\").Select()" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "id": "478f50a9", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "# pastes the clipboard to the currently selected cell\n", 152 | "ws1.Paste()" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "id": "ffb89893", 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "# reads the clipboard to a dataframe\n", 163 | "df = pd.read_clipboard()\n", 164 | "df" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "5fce000d", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "wb.Save()\n", 175 | "excel.Application.Quit()" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "id": "2027c95f", 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [] 185 | } 186 | ], 187 | "metadata": { 188 | "kernelspec": { 189 | "display_name": "Python 3", 190 | "language": "python", 191 | "name": "python3" 192 | }, 193 | "language_info": { 194 | "codemirror_mode": { 195 | "name": "ipython", 196 | "version": 3 197 | }, 198 | "file_extension": ".py", 199 | "mimetype": "text/x-python", 200 | "name": "python", 201 | "nbconvert_exporter": "python", 202 | "pygments_lexer": "ipython3", 203 | "version": "3.8.8" 204 | } 205 | }, 206 | "nbformat": 4, 207 | "nbformat_minor": 5 208 | } 209 | -------------------------------------------------------------------------------- /General/test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/General/test.xlsx -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/Images/long_data.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/Pivot_Tables_and_Table/Images/long_data.JPG -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/Images/long_data_pivot_table.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/Pivot_Tables_and_Table/Images/long_data_pivot_table.JPG -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/Images/pivot_table_fields.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/Pivot_Tables_and_Table/Images/pivot_table_fields.JPG -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/README.md: -------------------------------------------------------------------------------- 1 | ![repo image](pivot_table_repo_image.png) 2 | 3 | # Automate Excel to Create Pivot Tables and Tables with Python 4 | 5 | This project demonstrates how to use Python to automate the creation of pivot tables and regular Excel tables. 6 | 7 | ## Project Structure 8 | 9 | - **`pivot_table_automation.ipynb`**: A Jupyter Notebook used for testing and iterative development. 10 | - **`pivot_table_automation.py`**: A standalone Python script version for production or scheduled use. 11 | 12 | ## Utility Script 13 | 14 | This project references a small utility script `get_host_name`, which selects a directory based on the host machine. It's used for local path resolution and can be removed without affecting the core functionality. 15 | 16 | - You can find `get_host_name` here: 17 | [Utilities/get_hostname_ip.py](https://github.com/trenton3983/Utilities/blob/master/get_hostname_ip.py) 18 | -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/create_pivot_table_with_win32com.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How to Create a Pivot Table in Excel with the Python win32com Module\n", 8 | "\n", 9 | "- Notebook Author: [Trenton McKinney][1]\n", 10 | "- Jupyter Notebook: [create_pivot_table-with_win32com.ipynb][2]\n", 11 | "- This implementation is for Windows systems with Excel and Python 3.6 or greater.\n", 12 | "- The most helpful way to figure out the proper Excel methods to use, is record a step-by-step Macro in Excel, while creating a pivot table in the form you want.\n", 13 | "- This code is most useful for creating a pivot table that has to be run on a routine basis in a file with existing data.\n", 14 | "\n", 15 | "\n", 16 | " [1]: https://trenton3983.github.io/\n", 17 | " [2]: https://github.com/trenton3983/Excel_Automation_with_Python/tree/master/Pivot_Tables_and_Table" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## Excel Data & Pivot Table" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "- The example data is in the following long form\n", 32 | " - ![long_data][1]\n", 33 | "- The goal is to implement a python script to create the following Pivot Table\n", 34 | " - ![pivot_table][2]\n", 35 | "- These are the Pivot Table Fields\n", 36 | " - ![fields][3]\n", 37 | "\n", 38 | "\n", 39 | "\n", 40 | " [1]: https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/master/Pivot_Tables_and_Table/Images/long_data.JPG\n", 41 | " [2]: https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/master/Pivot_Tables_and_Table/Images/long_data_pivot_table.JPG\n", 42 | " [3]: https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/master/Pivot_Tables_and_Table/Images/pivot_table_fields.JPG" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "# Python Code" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Imports" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "import win32com.client as win32\n", 66 | "from pywintypes import com_error\n", 67 | "from pathlib import Path\n", 68 | "import sys\n", 69 | "import pandas as pd # only used for synthetic data\n", 70 | "import numpy as np # only used for synthetic data\n", 71 | "import random # only used for synthetic data\n", 72 | "from datetime import datetime # only used for synthetic data\n", 73 | "\n", 74 | "win32c = win32.constants" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## Function to create synthetic data\n", 82 | "\n", 83 | "- This function is only required to create the test data" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "def create_test_excel_file(f_path: Path, f_name: str, sheet_name: str):\n", 93 | " \n", 94 | " filename = f_path / f_name\n", 95 | " random.seed(365)\n", 96 | " np.random.seed(365)\n", 97 | " number_of_data_rows = 1000\n", 98 | " \n", 99 | " # create list of 31 dates\n", 100 | " dates = pd.bdate_range(datetime(2020, 7, 1), freq='1d', periods=31).tolist()\n", 101 | "\n", 102 | " data = {'date': [random.choice(dates) for _ in range(number_of_data_rows)],\n", 103 | " 'expense': [random.choice(['business', 'personal']) for _ in range(number_of_data_rows)],\n", 104 | " 'products': [random.choice(['ribeye', 'coffee', 'salmon', 'pie']) for _ in range(number_of_data_rows)],\n", 105 | " 'price': np.random.normal(15, 5, size=(1, number_of_data_rows))[0]}\n", 106 | "\n", 107 | " # create the dataframe and save it to Excel\n", 108 | " pd.DataFrame(data).to_excel(filename, index=False, sheet_name=sheet_name, float_format='%.2f')" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "## Function to create the pivot table" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "def pivot_table(wb: object, ws1: object, pt_ws: object, ws_name: str, pt_name: str, pt_rows: list, pt_cols: list, pt_filters: list, pt_fields: list):\n", 125 | " \"\"\"\n", 126 | " wb = workbook1 reference\n", 127 | " ws1 = worksheet1\n", 128 | " pt_ws = pivot table worksheet number\n", 129 | " ws_name = pivot table worksheet name\n", 130 | " pt_name = name given to pivot table\n", 131 | " pt_rows, pt_cols, pt_filters, pt_fields: values selected for filling the pivot tables\n", 132 | " \"\"\"\n", 133 | "\n", 134 | " # pivot table location\n", 135 | " pt_loc = len(pt_filters) + 2\n", 136 | " \n", 137 | " # grab the pivot table source data\n", 138 | " pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=ws1.UsedRange)\n", 139 | " \n", 140 | " # create the pivot table object\n", 141 | " pc.CreatePivotTable(TableDestination=f'{ws_name}!R{pt_loc}C1', TableName=pt_name)\n", 142 | "\n", 143 | " # selecte the pivot table work sheet and location to create the pivot table\n", 144 | " pt_ws.Select()\n", 145 | " pt_ws.Cells(pt_loc, 1).Select()\n", 146 | "\n", 147 | " # Sets the rows, columns and filters of the pivot table\n", 148 | " for field_list, field_r in ((pt_filters, win32c.xlPageField), (pt_rows, win32c.xlRowField), (pt_cols, win32c.xlColumnField)):\n", 149 | " for i, value in enumerate(field_list):\n", 150 | " pt_ws.PivotTables(pt_name).PivotFields(value).Orientation = field_r\n", 151 | " pt_ws.PivotTables(pt_name).PivotFields(value).Position = i + 1\n", 152 | "\n", 153 | " # Sets the Values of the pivot table\n", 154 | " for field in pt_fields:\n", 155 | " pt_ws.PivotTables(pt_name).AddDataField(pt_ws.PivotTables(pt_name).PivotFields(field[0]), field[1], field[2]).NumberFormat = field[3]\n", 156 | "\n", 157 | " # Visiblity True or Valse\n", 158 | " pt_ws.PivotTables(pt_name).ShowValuesRow = True\n", 159 | " pt_ws.PivotTables(pt_name).ColumnGrand = True" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "## Function to create Excel com object\n", 167 | "\n", 168 | "- To modify this code for a new data file, update:\n", 169 | " - `ws1`\n", 170 | " - `ws2_name`\n", 171 | " - `pt_name`\n", 172 | " - `pt_rows`\n", 173 | " - `pt_cols`\n", 174 | " - `pt_filters`\n", 175 | " - `pt_fields`" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "def run_excel(f_path: Path, f_name: str, sheet_name: str):\n", 185 | "\n", 186 | " filename = f_path / f_name\n", 187 | "\n", 188 | " # create excel object\n", 189 | " excel = win32.gencache.EnsureDispatch('Excel.Application')\n", 190 | "\n", 191 | " # excel can be visible or not\n", 192 | " excel.Visible = True # False\n", 193 | " \n", 194 | " # try except for file / path\n", 195 | " try:\n", 196 | " wb = excel.Workbooks.Open(filename)\n", 197 | " except com_error as e:\n", 198 | " if e.excepinfo[5] == -2146827284:\n", 199 | " print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')\n", 200 | " else:\n", 201 | " raise e\n", 202 | " sys.exit(1)\n", 203 | "\n", 204 | " # set worksheet\n", 205 | " ws1 = wb.Sheets('data')\n", 206 | " \n", 207 | " # Setup and call pivot_table\n", 208 | " ws2_name = 'pivot_table'\n", 209 | " wb.Sheets.Add().Name = ws2_name\n", 210 | " ws2 = wb.Sheets(ws2_name)\n", 211 | " \n", 212 | " pt_name = 'example' # must be a string\n", 213 | " pt_rows = ['expense'] # must be a list\n", 214 | " pt_cols = ['products'] # must be a list\n", 215 | " pt_filters = ['date'] # must be a list\n", 216 | " # [0]: field name [1]: pivot table column name [3]: calulation method [4]: number format\n", 217 | " pt_fields = [['price', 'price: mean', win32c.xlAverage, '$#,##0.00'], # must be a list of lists\n", 218 | " ['price', 'price: sum', win32c.xlSum, '$#,##0.00'],\n", 219 | " ['price', 'price: count', win32c.xlCount, '0']]\n", 220 | " \n", 221 | " pivot_table(wb, ws1, ws2, ws2_name, pt_name, pt_rows, pt_cols, pt_filters, pt_fields)\n", 222 | " \n", 223 | "# wb.Close(True)\n", 224 | "# excel.Quit()" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "## Main function to call other functions\n", 232 | "\n", 233 | "- To modify this code for a new data file, update:\n", 234 | " - `sheet_name`\n", 235 | " - `f_path`\n", 236 | " - `f_name`\n", 237 | " - Remove `create_test_excel_file`" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "def main():\n", 247 | " # sheet name for data\n", 248 | " sheet_name = 'data' # update with sheet name from your file\n", 249 | " # file path\n", 250 | " f_path = Path.cwd() # file in current working directory\n", 251 | "# f_path = Path(r'c:\\...\\Documents') # file located somewhere else\n", 252 | " # excel file\n", 253 | " f_name = 'test.xlsx'\n", 254 | " \n", 255 | " # function calls\n", 256 | " create_test_excel_file(f_path, f_name, sheet_name) # remove when running your own file\n", 257 | " run_excel(f_path, f_name, sheet_name)" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "## Call `def main`" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "main()" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "# Visual Basic\n", 281 | "\n", 282 | "- Following is the visual basic code recorded while manually creating the pivot table" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "## Select Source Data\n", 290 | "\n", 291 | "```vbnet\n", 292 | "Range(\"A1:D1\").Select\n", 293 | "Range(Selection, Selection.End(xlDown)).Select\n", 294 | "```" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "## Add Pivot Table\n", 302 | "\n", 303 | "```vbnet\n", 304 | "Sheets.Add\n", 305 | "ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _\n", 306 | " \"data!R1C1:R1001C4\", Version:=6).CreatePivotTable TableDestination:= _\n", 307 | " \"Sheet1!R3C1\", TableName:=\"PivotTable1\", DefaultVersion:=6\n", 308 | "```" 309 | ] 310 | }, 311 | { 312 | "cell_type": "markdown", 313 | "metadata": {}, 314 | "source": [ 315 | "## Select Worksheet, Rename, Select Cells, Select Range\n", 316 | "\n", 317 | "```vbnet\n", 318 | "Sheets(\"Sheet1\").Select\n", 319 | "Sheets(\"Sheet1\").Name = \"pivot_table\"\n", 320 | "Cells(3, 1).Select\n", 321 | "Range(\"A3\").Select\n", 322 | "```" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "## Create Filters\n", 330 | "\n", 331 | "```vbnet\n", 332 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"date\")\n", 333 | " .Orientation = xlPageField\n", 334 | " .Position = 1\n", 335 | "```" 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "## Create Columns\n", 343 | "\n", 344 | "```vbnet\n", 345 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"products\")\n", 346 | " .Orientation = xlColumnField\n", 347 | " .Position = 1\n", 348 | "```" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "## Create Rows\n", 356 | "\n", 357 | "```vbnet\n", 358 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"expense\")\n", 359 | " .Orientation = xlRowField\n", 360 | " .Position = 1\n", 361 | "```" 362 | ] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": {}, 367 | "source": [ 368 | "## Create Values" 369 | ] 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "### Price Sum\n", 376 | "\n", 377 | "```vbnet\n", 378 | "ActiveSheet.PivotTables(\"PivotTable1\").AddDataField ActiveSheet.PivotTables( _\n", 379 | " \"PivotTable1\").PivotFields(\"price\"), \"Sum of price\", xlSum\n", 380 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"Sum of price\")\n", 381 | " .Caption = \"price: sum\"\n", 382 | " .NumberFormat = \"$#,##0.00\"\n", 383 | "```" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": {}, 389 | "source": [ 390 | "### Price Mean\n", 391 | "\n", 392 | "```vbnet\n", 393 | "ActiveSheet.PivotTables(\"PivotTable1\").AddDataField ActiveSheet.PivotTables( _\n", 394 | " \"PivotTable1\").PivotFields(\"price\"), \"Sum of price\", xlSum\n", 395 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"Sum of price\")\n", 396 | " .Caption = \"price: mean\"\n", 397 | " .Function = xlAverage\n", 398 | " .NumberFormat = \"$#,##0.00\"\n", 399 | "```" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "### Price Count\n", 407 | "\n", 408 | "```vbnet\n", 409 | "ActiveSheet.PivotTables(\"PivotTable1\").AddDataField ActiveSheet.PivotTables( _\n", 410 | " \"PivotTable1\").PivotFields(\"price\"), \"Sum of price\", xlSum\n", 411 | "With ActiveSheet.PivotTables(\"PivotTable1\").PivotFields(\"Sum of price\")\n", 412 | " .Caption = \"price: count\"\n", 413 | " .Function = xlCount\n", 414 | " .NumberFormat = \"0\"\n", 415 | "```" 416 | ] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": {}, 421 | "source": [ 422 | "# Resources\n", 423 | "\n", 424 | "- [Automate Excel with Python][1]\n", 425 | " - Examples with Pivot Table\n", 426 | "- [Using Python win32com to get list of Excel worksheets][2]\n", 427 | "- [How to create a pivot table in Excel with python win32com][6]\n", 428 | "- [Excel VBA reference][3]\n", 429 | "- [Workbook object (Excel)][4]\n", 430 | "- [Worksheet object (Excel)][5]\n", 431 | "- [Excel Clipboard to DataFrame][7]\n", 432 | "\n", 433 | "\n", 434 | " [1]: https://github.com/trenton3983/Excel_Automation_with_Python\n", 435 | " [2]: https://stackoverflow.com/questions/62505403/using-python-win32com-to-get-list-of-excel-worksheets\n", 436 | " [3]: https://docs.microsoft.com/en-us/office/vba/api/overview/excel\n", 437 | " [4]: https://docs.microsoft.com/en-us/office/vba/api/excel.workbook\n", 438 | " [5]: https://docs.microsoft.com/en-us/office/vba/api/excel.worksheet\n", 439 | " [6]: https://stackoverflow.com/questions/62509367/how-to-create-a-pivot-table-in-excel-with-python-win32com\n", 440 | " [7]: https://github.com/trenton3983/Excel_Automation_with_Python/blob/master/General/excel_clipboard_to_dataframe.ipynb" 441 | ] 442 | } 443 | ], 444 | "metadata": { 445 | "kernelspec": { 446 | "display_name": "Python 3 (ipykernel)", 447 | "language": "python", 448 | "name": "python3" 449 | }, 450 | "language_info": { 451 | "codemirror_mode": { 452 | "name": "ipython", 453 | "version": 3 454 | }, 455 | "file_extension": ".py", 456 | "mimetype": "text/x-python", 457 | "name": "python", 458 | "nbconvert_exporter": "python", 459 | "pygments_lexer": "ipython3", 460 | "version": "3.11.5" 461 | } 462 | }, 463 | "nbformat": 4, 464 | "nbformat_minor": 4 465 | } 466 | -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/pivot_table_repo_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/Pivot_Tables_and_Table/pivot_table_repo_image.png -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/pivot_tables.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Create Excel Objects and Create Pivot Tables in Excel" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import win32com.client as win32\n", 17 | "import sys\n", 18 | "import os\n", 19 | "from pythoncom import com_error\n", 20 | "\n", 21 | "win32c = win32.constants" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "def pivot_table(wb, ws1, pt_ws, num_test_cond, ws_name, pt_name, pt_rows, pt_filters, pt_fields):\n", 31 | " \"\"\"\n", 32 | " wb = workbook1 reference\n", 33 | " ws1 = worksheet1\n", 34 | " pt_ws = pivot table worksheet number\n", 35 | " num_test_cond = number of unique TestCondition1 values (use for determine row locations)\n", 36 | " ws_name = worksheet name\n", 37 | " pt_name = name given to pivot table\n", 38 | " pt_rows, pt_filters, pt_fields: values selected for filling the pivot tables\n", 39 | " \"\"\"\n", 40 | "\n", 41 | " pt_loc = len(pt_filters) + 2\n", 42 | " \n", 43 | " pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=ws1.UsedRange)\n", 44 | " pc.CreatePivotTable(TableDestination=f'{ws_name}!R{pt_loc}C1', TableName=pt_name)\n", 45 | "\n", 46 | " pt_ws.Select()\n", 47 | " pt_ws.Cells(pt_loc, 1).Select()\n", 48 | "\n", 49 | " \"\"\"Sets the rows and filters of the pivot table\"\"\"\n", 50 | "\n", 51 | " for field_list, field_c in ((pt_filters, win32c.xlPageField), (pt_rows, win32c.xlRowField)):\n", 52 | " for i, value in enumerate(field_list):\n", 53 | " pt_ws.PivotTables(pt_name).PivotFields(value).Orientation = field_c\n", 54 | " pt_ws.PivotTables(pt_name).PivotFields(value).Position = i + 1\n", 55 | "\n", 56 | " \"\"\"Sets the Values of the pivot table\"\"\"\n", 57 | "\n", 58 | " for field in pt_fields:\n", 59 | " pt_ws.PivotTables(pt_name).AddDataField(pt_ws.PivotTables(pt_name).PivotFields(field[0]), field[1], field[2])\n", 60 | "\n", 61 | " \n", 62 | " pt_ws.PivotTables(pt_name).ShowValuesRow = False\n", 63 | " pt_ws.PivotTables(pt_name).ColumnGrand = False\n", 64 | "\n", 65 | " \"\"\"Hides details under each row section - generic form\"\"\"\n", 66 | " \n", 67 | " row_first_chart_value = pt_loc + 1\n", 68 | " row_last_chart_value = row_first_chart_value + num_test_cond # this is used to hide detail in the PT\n", 69 | " \n", 70 | " '''\n", 71 | " for x in range(row_first_chart_value, row_last_chart_value, 1):\n", 72 | " pt_ws.Cells(x, 1).Select()\n", 73 | " cell_value = (str(pt_ws.Cells(x, 1).Value)).rstrip('0').rstrip('.')\n", 74 | " pt_ws.PivotTables(pt_name).PivotFields('TestCondition1').PivotItems(f'{cell_value}').ShowDetail = False\n", 75 | " '''\n", 76 | " \n", 77 | " return row_first_chart_value, row_last_chart_value\n" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "def run_excel():\n", 87 | "\n", 88 | " filename = os.path.join(f_path, f_name)\n", 89 | " excel = win32.gencache.EnsureDispatch('Excel.Application')\n", 90 | " excel.Visible = True\n", 91 | " try:\n", 92 | " wb = excel.Workbooks.Open(filename)\n", 93 | " except com_error as e:\n", 94 | " if e.excepinfo[5] == -2146827284:\n", 95 | " print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')\n", 96 | " else:\n", 97 | " raise e\n", 98 | " sys.exit(1)\n", 99 | " \n", 100 | " ws1 = wb.Sheets('Sheet1')\n", 101 | " \n", 102 | " \"\"\"Determine number of unique TestConditon1 values\"\"\"\n", 103 | " last_r = ws1.UsedRange.Rows.Count\n", 104 | " column_values = set()\n", 105 | " for x in range(2, last_r + 1, 1):\n", 106 | " column_values.add(ws1.Range(f'N{x}').Value)\n", 107 | " unique_testconditions = len(column_values)\n", 108 | " \n", 109 | " worksheet_name2 = 'Average_of_Max'\n", 110 | " wb.Sheets.Add().Name = worksheet_name2\n", 111 | " ws2 = wb.Sheets(worksheet_name2)\n", 112 | " row_first_chart_value2, row_last_chart_value2 = pivot_table(wb, ws1, ws2, unique_testconditions,\n", 113 | " ws_name = worksheet_name2,\n", 114 | " pt_name = 'PivotTable1',\n", 115 | " pt_rows = ['TestCondition1', 'TestCondition2'],\n", 116 | " pt_filters = ['Aux(V)', 'Main(V)', 'Temp(C)'],\n", 117 | " pt_fields = [['Max_I_TC1(A)', 'Avgerage of Max_I_TC1(A)', win32c.xlAverage],\n", 118 | " ['Max_I_TC2(A)', 'Avgerage of Max_I_TC2(A)', win32c.xlAverage],\n", 119 | " ['Total_Pwr(W)', 'Avgerage of Total_Pwr(W)', win32c.xlAverage]])\n", 120 | "\n", 121 | " worksheet_name3 = 'Max_of_Max'\n", 122 | " wb.Sheets.Add().Name = worksheet_name3\n", 123 | " ws3 = wb.Sheets(worksheet_name3)\n", 124 | " row_first_chart_value3, row_last_chart_value3 = pivot_table(wb, ws1, ws3, unique_testconditions,\n", 125 | " ws_name = worksheet_name3,\n", 126 | " pt_name = 'PivotTable2',\n", 127 | " pt_rows = ['TestCondition1', 'TestCondition2'],\n", 128 | " pt_filters = [],\n", 129 | " pt_fields = [['Max_I_TC1(A)', 'Max of Max_I_TC1(A)', win32c.xlMax],\n", 130 | " ['Max_I_TC2(A)', 'Max of Max_I_TC2(A)', win32c.xlMax],\n", 131 | " ['Total_Pwr(W)', 'Max of Total_Pwr(W)', win32c.xlMax]])\n", 132 | "\n", 133 | " print(row_first_chart_value2, row_last_chart_value2, row_first_chart_value3, row_last_chart_value3)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "## Save the test file and close it - manually\n", 141 | "### Restart the kernel and clear the output from the Kernel menu at the top" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "# Create a table from the data for the report" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "The section is for testing the implementaion of the table creation code so does not use methods (as above) to work with the Excel objects. \n", 156 | "\n", 157 | "The Excel objects below are created so each subsequent cell can use the object, which is easier for testing a specific implemention of some Excel method (i.e. all the code doesn't need to be run each time like above), as only the snippet being tested in a cell requires running." 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "import win32com.client as win32\n", 167 | "import sys\n", 168 | "import os\n", 169 | "from pythoncom import com_error\n", 170 | "\n", 171 | "win32c = win32.constants" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "f_name = 'pivot_table.xlsx'\n", 181 | "f_path = r'C:\\PythonProjects\\Excel_Automation_with_Python\\Pivot_Tables_and_Table'" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "filename = os.path.join(f_path, f_name)\n", 191 | "excel = win32.gencache.EnsureDispatch('Excel.Application')\n", 192 | "excel.Visible = True\n", 193 | "try:\n", 194 | " wb = excel.Workbooks.Open(filename)\n", 195 | "except com_error as e:\n", 196 | " if e.excepinfo[5] == -2146827284:\n", 197 | " print(f'Failed to open spreadsheet. Invalid filename or location: {filename}')\n", 198 | " else:\n", 199 | " raise e\n", 200 | " sys.exit(1)\n", 201 | "\n", 202 | "ws1 = wb.Sheets('Sheet1')\n", 203 | "ws2 = wb.Sheets('Average_of_Max')\n", 204 | "ws3 = wb.Sheets('Max_of_Max')" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | "\"\"\"Looks at ws1 (Dual_Edge_Power), Column N (TestCondition1) and returns a set of unique values\"\"\"\n", 214 | "last_r = ws1.UsedRange.Rows.Count\n", 215 | "column_values = set()\n", 216 | "\n", 217 | "for x in range(2, last_r + 1, 1):\n", 218 | " column_values.add(ws1.Range(f'N{x}').Value)\n", 219 | " \n", 220 | "print(f'Number of Test Condition1 values: {len(column_values)}')\n", 221 | "print(f'Test Condition1: {column_values}')" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "\"\"\"Used for Testing\"\"\"\n", 231 | "ws2_first_data_row = 6\n", 232 | "ws2_last_data_row = 13\n", 233 | "ws3_first_data_row = 3\n", 234 | "ws3_last_data_row= 10" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "\"\"\"Create EV Report Table worksheet\"\"\"\n", 244 | "worksheet_ev_table = 'EV_Report_Table'\n", 245 | "wb.Sheets.Add().Name = worksheet_ev_table\n", 246 | "ws4 = wb.Sheets(worksheet_ev_table)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": null, 252 | "metadata": {}, 253 | "outputs": [], 254 | "source": [ 255 | "\"\"\"Table Headers\"\"\"\n", 256 | "table_headers = ['Test Point', 'Typical I (A)', 'Maximum I (A)', 'Max Total Power (W)', 'Spec', 'Status']\n", 257 | "\n", 258 | "for x, col_header in enumerate(table_headers):\n", 259 | " ws4.Cells(2, x + 2).Value = col_header" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "\"\"\"Create a list of Test Point(s) (table row headers) from ws3 (Max of Max)\"\"\"\n", 269 | "test_points = []\n", 270 | "for x in range(ws3_first_data_row, ws3_last_data_row + 1):\n", 271 | " test_points.append(ws3.Cells(x, 1).Value)" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "\"\"\"Write Table row header formulas\"\"\"\n", 281 | "for x in range(ws3_first_data_row, ws3_last_data_row + 1):\n", 282 | " ws4.Cells(x, 2).Value = f'=Max_of_Max!A{x}'" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": null, 288 | "metadata": {}, 289 | "outputs": [], 290 | "source": [ 291 | "\"\"\"Set column filters and copy specification (tolerance) value\"\"\"\n", 292 | "\n", 293 | "\n", 294 | "def get_tol_address(i, test_condition):\n", 295 | " \n", 296 | " ws1.Activate() # set Dual_Edge_Power (data worksheet) as active\n", 297 | "\n", 298 | " if i % 2 == 0:\n", 299 | " col_select = [14, 'Q1']\n", 300 | " else:\n", 301 | " col_select = [19, 'V1']\n", 302 | "\n", 303 | " ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all)\n", 304 | " ws1.UsedRange.AutoFilter(col_select[0], test_condition) # set specific Column filter\n", 305 | " ws1.Range(col_select[1]).End(win32c.xlDown).Select() # select last cell in the I_Tol column (Q or V)\n", 306 | " cell_address = excel.Selection.Address # get the selected cell address\n", 307 | " # print(cell_address)\n", 308 | " # print(ws1.Range(f'{cell_address}').Value)\n", 309 | " ws1.Range(col_select[1]).End(win32c.xlUp).Select() # reset to rol 1\n", 310 | " ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all)\n", 311 | "\n", 312 | " return cell_address\n" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "\"\"\"Write Maximum I (A), Typical I (A), and Max Total Power (W) cell equations\"\"\"\n", 322 | "for x, row_header in enumerate(test_points):\n", 323 | " \n", 324 | " tol_address = get_tol_address(x, row_header)\n", 325 | " if x % 2 == 0:\n", 326 | " ws4.Cells(x + 3, 4).Value = f'=GETPIVOTDATA(\"Max of Max_I_TC1(A)\",Max_of_Max!R2C1,\"TestCondition1\",\"{row_header}\")'\n", 327 | " ws4.Cells(x + 3, 3).Value = f'=GETPIVOTDATA(\"Avgerage of Max_I_TC1(A)\",Average_of_Max!R5C1,\"TestCondition1\",\"{row_header}\")'\n", 328 | " ws4.Cells(x + 3, 5).Value = f'=GETPIVOTDATA(\"Max of Total_Pwr(W)\",Max_of_Max!R2C1,\"TestCondition1\",\"{row_header}\")'\n", 329 | " else:\n", 330 | " ws4.Cells(x + 3, 4).Value = f'=GETPIVOTDATA(\"Max of Max_I_TC2(A)\",Max_of_Max!R2C1,\"TestCondition1\",\"{test_points[x - 1]}\")'\n", 331 | " ws4.Cells(x + 3, 3).Value = f'=GETPIVOTDATA(\"Avgerage of Max_I_TC2(A)\",Average_of_Max!R5C1,\"TestCondition1\",\"{test_points[x - 1]}\")'\n", 332 | " \n", 333 | " ws4.Cells(x + 3, 6).Value = f'=Sheet1!{tol_address}'\n", 334 | " ws4.Cells(x + 3, 7).Value = f'=if(D{x + 3}>F{x + 3},\"Fail\",\"Pass\")'\n", 335 | " \n", 336 | "ws4.Activate() # set the EV Report Table worksheet as active" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "# Format the Table" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "\"\"\"Merge and Center Max Total Power (W) column\"\"\"\n", 353 | "for x in range(ws3_first_data_row, ws3_last_data_row + 1, 2):\n", 354 | " ws4.Range(f'E{x}:E{x + 1}').Merge()\n", 355 | " ws4.Range(f'E{x}:E{x + 1}').HorizontalAlignment = win32c.xlCenter\n", 356 | " ws4.Range(f'E{x}:E{x + 1}').VerticalAlignment = win32c.xlCenter" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "last_table_row = len(test_points) + 2\n", 366 | "\n", 367 | "\"\"\"Format Numbers\"\"\"\n", 368 | "ws4.Range(f'C3:F{last_table_row}').Select()\n", 369 | "excel.Selection.NumberFormat = \"0.000\"\n", 370 | "\n", 371 | "\"\"\"Autofit Width\"\"\"\n", 372 | "ws4.Columns('B:G').EntireColumn.AutoFit()\n" 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": null, 378 | "metadata": { 379 | "tags": [] 380 | }, 381 | "outputs": [], 382 | "source": [ 383 | "\"\"\"Conditional Formatting\"\"\"\n", 384 | "# ws4.Range(f'G3:G{last_table_row}').Select()\n", 385 | "\n", 386 | "'Condition, Font Color, Fill Color'\n", 387 | "conditional_formatting = {'Pass': [-16752384, 13561798],\n", 388 | " 'Fail': [-16383844, 13551615]}\n", 389 | "\n", 390 | "for k, v in conditional_formatting.items():\n", 391 | " ws4.Range(f'G3:G{last_table_row}').Select()\n", 392 | " excel.Selection.FormatConditions.Add(Type=win32c.xlTextString, TextOperator=win32c.xlContains, String=k)\n", 393 | " excel.Selection.FormatConditions(excel.Selection.FormatConditions.Count).SetFirstPriority()\n", 394 | " excel.Selection.FormatConditions(1).Font.Color = v[0]\n", 395 | " excel.Selection.FormatConditions(1).Interior.PatternColorIndex = win32c.xlAutomatic\n", 396 | " excel.Selection.FormatConditions(1).Interior.Color = v[1]\n", 397 | "\n" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": null, 403 | "metadata": { 404 | "tags": [] 405 | }, 406 | "outputs": [], 407 | "source": [ 408 | "\"\"\"Set the Table Boarders\"\"\"\n", 409 | "border_types = {win32c.xlEdgeLeft : win32c.xlMedium, win32c.xlEdgeTop : win32c.xlMedium, win32c.xlEdgeBottom : win32c.xlMedium,\n", 410 | " win32c.xlEdgeRight : win32c.xlMedium, win32c.xlInsideVertical : win32c.xlThin, win32c.xlInsideHorizontal : win32c.xlThin}\n", 411 | "\n", 412 | "for k, v in border_types.items():\n", 413 | " ws4.Range(f'B2:G{last_table_row}').Select()\n", 414 | " excel.Selection.Borders(k).Weight = v" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "# Examples" 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": null, 427 | "metadata": {}, 428 | "outputs": [], 429 | "source": [ 430 | "\"\"\"Determine last row used\"\"\" \n", 431 | "used = ws3.UsedRange # create the UsedRange object\n", 432 | "# used.Row -> returns first row used, Rows.Count -> number of row used\n", 433 | "nrows = used.Row + used.Rows.Count - 1\n", 434 | "nrows # -> last row with a value in it" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": null, 440 | "metadata": { 441 | "scrolled": true, 442 | "tags": [] 443 | }, 444 | "outputs": [], 445 | "source": [ 446 | "\"\"\"Set column filters and copy specification (tolerance) value\"\"\"\n", 447 | "for i, test_point in enumerate(test_points):\n", 448 | " if i % 2 == 0:\n", 449 | " col_select = [14, 'Q1']\n", 450 | " else:\n", 451 | " col_select = [19, 'V1']\n", 452 | "\n", 453 | " ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all)\n", 454 | " ws1.UsedRange.AutoFilter(col_select[0], test_point) # set specific Column filter\n", 455 | " ws1.Range(col_select[1]).End(win32c.xlDown).Select() # select last cell in the I_Tol column (Q or V)\n", 456 | " cell_address = excel.Selection.Address # get the selected cell address\n", 457 | " print(cell_address)\n", 458 | " print(ws1.Range(f'{cell_address}').Value)\n", 459 | " ws1.Range(col_select[1]).End(win32c.xlUp).Select() # reset to rol 1\n", 460 | " ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all)\n" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": null, 466 | "metadata": { 467 | "tags": [] 468 | }, 469 | "outputs": [], 470 | "source": [ 471 | "\"\"\"List comprehension testing - not used for anything specific\"\"\"\n", 472 | "poop = ['10G_LR_12V_MAIN_NCSI depop', 'LR_3P3V_AUX_D0']\n", 473 | "for x in poop:\n", 474 | " for y in ['_MAIN', '_AUX']:\n", 475 | " if y in x.upper():\n", 476 | " pooo = ((x.upper()).split(y))[0].replace('_', ' ')\n", 477 | " print(pooo)\n" 478 | ] 479 | } 480 | ], 481 | "metadata": { 482 | "kernelspec": { 483 | "display_name": "Python 3", 484 | "language": "python", 485 | "name": "python3" 486 | }, 487 | "language_info": { 488 | "codemirror_mode": { 489 | "name": "ipython", 490 | "version": 3 491 | }, 492 | "file_extension": ".py", 493 | "mimetype": "text/x-python", 494 | "name": "python", 495 | "nbconvert_exporter": "python", 496 | "pygments_lexer": "ipython3", 497 | "version": "3.8.8" 498 | } 499 | }, 500 | "nbformat": 4, 501 | "nbformat_minor": 4 502 | } 503 | -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/pivot_tables.py: -------------------------------------------------------------------------------- 1 | import win32com.client as win32 2 | import sys 3 | import os 4 | from get_hostname_ip import get_host_name_ip 5 | from pythoncom import com_error 6 | 7 | win32c = win32.constants 8 | 9 | 10 | def pivot_table(wb, ws1, pt_ws, num_test_cond, ws_name, pt_name, pt_rows, pt_filters, pt_fields): 11 | """ 12 | wb = workbook1 reference 13 | ws1 = worksheet1 14 | pt_ws = pivot table worksheet number 15 | num_test_cond = number of unique TestCondition1 values (use for determine row locations) 16 | ws_name = worksheet name 17 | pt_name = name given to pivot table 18 | pt_rows, pt_filters, pt_fields: values selected for filling the pivot tables 19 | """ 20 | 21 | pt_loc = len(pt_filters) + 2 # the table begins 2 rows below the filters 22 | 23 | pc = wb.PivotCaches().Create(SourceType=win32c.xlDatabase, SourceData=ws1.UsedRange) 24 | pc.CreatePivotTable(TableDestination=f'{ws_name}!R{pt_loc}C1', TableName=pt_name) 25 | 26 | pt_ws.Select() 27 | pt_ws.Cells(pt_loc, 1).Select() 28 | 29 | """Sets the rows and filters of the pivot table""" 30 | 31 | for field_list, field_c in ((pt_filters, win32c.xlPageField), (pt_rows, win32c.xlRowField)): 32 | for i, value in enumerate(field_list): 33 | pt_ws.PivotTables(pt_name).PivotFields(value).Orientation = field_c 34 | pt_ws.PivotTables(pt_name).PivotFields(value).Position = i + 1 35 | 36 | """Sets the Values of the pivot table""" 37 | 38 | for field in pt_fields: 39 | pt_ws.PivotTables(pt_name).AddDataField(pt_ws.PivotTables(pt_name).PivotFields(field[0]), field[1], field[2]) 40 | 41 | pt_ws.PivotTables(pt_name).ShowValuesRow = False 42 | pt_ws.PivotTables(pt_name).ColumnGrand = False 43 | 44 | """Hides details under each row section - generic form""" 45 | 46 | row_first_chart_value = pt_loc + 1 47 | row_last_chart_value = row_first_chart_value + num_test_cond # this is used to hide detail in the PT 48 | 49 | ''' 50 | for x in range(row_first_chart_value, row_last_chart_value, 1): 51 | pt_ws.Cells(x, 1).Select() 52 | cell_value = (str(pt_ws.Cells(x, 1).Value)).rstrip('0').rstrip('.') 53 | pt_ws.PivotTables(pt_name).PivotFields('TestCondition1').PivotItems(f'{cell_value}').ShowDetail = False 54 | ''' 55 | 56 | return row_first_chart_value, row_last_chart_value 57 | 58 | 59 | def run_excel(f_path, f_name): 60 | filename = os.path.join(f_path, f_name) 61 | excel = win32.gencache.EnsureDispatch('Excel.Application') 62 | excel.Visible = True 63 | try: 64 | wb = excel.Workbooks.Open(filename) 65 | except com_error as e: 66 | if e.excepinfo[5] == -2146827284: 67 | print(f'Failed to open spreadsheet. Invalid filename or location: {filename}') 68 | else: 69 | raise e 70 | sys.exit(1) 71 | 72 | ws1 = wb.Sheets('Sheet1') 73 | 74 | """Determine number of unique TestCondition1 values""" 75 | last_r = ws1.UsedRange.Rows.Count 76 | column_values = set() 77 | for x in range(2, last_r + 1, 1): 78 | column_values.add(ws1.Range(f'N{x}').Value) 79 | unique_test_conditions = len(column_values) 80 | 81 | worksheet_name2 = 'Average_of_Max' 82 | wb.Sheets.Add().Name = worksheet_name2 83 | ws2 = wb.Sheets(worksheet_name2) 84 | row_first_chart_value2, _ =\ 85 | pivot_table(wb, ws1, ws2, unique_test_conditions, 86 | ws_name=worksheet_name2, 87 | pt_name='PivotTable1', 88 | pt_rows=['TestCondition1', 'TestCondition2'], 89 | pt_filters=['Aux(V)', 'Main(V)', 'Temp(C)'], 90 | pt_fields=[['Max_I_TC1(A)', 'Avgerage of Max_I_TC1(A)', win32c.xlAverage], 91 | ['Max_I_TC2(A)', 'Avgerage of Max_I_TC2(A)', win32c.xlAverage], 92 | ['Total_Pwr(W)', 'Avgerage of Total_Pwr(W)', win32c.xlAverage]]) 93 | 94 | worksheet_name3 = 'Max_of_Max' 95 | wb.Sheets.Add().Name = worksheet_name3 96 | ws3 = wb.Sheets(worksheet_name3) 97 | row_first_chart_value3, _ =\ 98 | pivot_table(wb, ws1, ws3, unique_test_conditions, 99 | ws_name=worksheet_name3, 100 | pt_name='PivotTable2', 101 | pt_rows=['TestCondition1', 'TestCondition2'], 102 | pt_filters=[], 103 | pt_fields=[['Max_I_TC1(A)', 'Max of Max_I_TC1(A)', win32c.xlMax], 104 | ['Max_I_TC2(A)', 'Max of Max_I_TC2(A)', win32c.xlMax], 105 | ['Total_Pwr(W)', 'Max of Total_Pwr(W)', win32c.xlMax]]) 106 | 107 | pt_row_positions = [row_first_chart_value2, row_first_chart_value3] 108 | ev_report_table(excel, wb, ws1, ws3, pt_row_positions) 109 | 110 | 111 | def ev_report_table(excel, wb, ws1, ws3, row_list): 112 | """ 113 | This method creats the report table 114 | :param excel: Excel object 115 | :param wb: Excel workbook object 116 | :param ws1: Excel worksheet objects 117 | :param ws3: Excel worksheet objects 118 | :param row_list: List of fist/last data cells for each pivot table 119 | :return: 120 | """ 121 | 122 | used = ws3.UsedRange # create the UsedRange object 123 | ws3_first_data_row = row_list[1] 124 | ws3_last_data_row = used.Row + used.Rows.Count - 1 # used.Row -> returns first row used, Rows.Count -> rows used 125 | 126 | """Create Report Table worksheet""" 127 | worksheet_ev_table = 'EV_Report_Table' 128 | wb.Sheets.Add().Name = worksheet_ev_table 129 | ws4 = wb.Sheets(worksheet_ev_table) 130 | 131 | """Table Headers""" 132 | table_headers = ['Test Point', 'Typical I (A)', 'Maximum I (A)', 'Max Total Power (W)', 'Spec', 'Status'] 133 | 134 | for x, col_header in enumerate(table_headers): 135 | cell = ws4.Cells(2, x + 2) 136 | cell.Value = col_header 137 | cell.Select() 138 | excel.Selection.Font.Bold = True 139 | 140 | """Create a list of Test Point(s) (table row headers) from ws3 (Max of Max)""" 141 | test_points = [] 142 | for x in range(ws3_first_data_row, ws3_last_data_row + 1): 143 | test_points.append(ws3.Cells(x, 1).Value) 144 | 145 | """Write Table row header formulas""" 146 | for x in range(ws3_first_data_row, ws3_last_data_row + 1): 147 | ws4.Cells(x, 2).Value = f'=Max_of_Max!A{x}' 148 | 149 | """Set column filters and copy specification (tolerance) value""" 150 | def get_tol_address(i, test_condition): 151 | """ 152 | Set column filters and copy specification (tolerance) value 153 | :param i: test condition index of the list being fed into this method 154 | :param test_condition: this is the parameter being used to set the Excel filter 155 | :return: returns a Excel cell address for the tolerance data 156 | """ 157 | 158 | ws1.Activate() # set the worksheet to be filtered as active 159 | 160 | if i % 2 == 0: 161 | col_select = [14, 'Q1'] 162 | else: 163 | col_select = [19, 'V1'] 164 | 165 | ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all) 166 | ws1.UsedRange.AutoFilter(col_select[0], test_condition) # set specific Column filter 167 | ws1.Range(col_select[1]).End(win32c.xlDown).Select() # select last cell in the I_Tol column (Q or V) 168 | cell_address = excel.Selection.Address # get the selected cell address 169 | ws1.Range(col_select[1]).End(win32c.xlUp).Select() # reset to rol 1 170 | ws1.UsedRange.AutoFilter(col_select[0]) # remove column filter (set to all) 171 | 172 | return cell_address 173 | 174 | """Write Maximum I (A), Typical I (A), and Max Total Power (W) cell equations""" 175 | for x, row_header in enumerate(test_points): 176 | tol_address = get_tol_address(x, row_header) 177 | if x % 2 == 0: 178 | ws4.Cells(x + 3, 4).Value =\ 179 | f'=GETPIVOTDATA("Max of Max_I_TC1(A)",Max_of_Max!R2C1,"TestCondition1","{row_header}")' 180 | ws4.Cells(x + 3, 3).Value =\ 181 | f'=GETPIVOTDATA("Avgerage of Max_I_TC1(A)",Average_of_Max!R5C1,"TestCondition1","{row_header}")' 182 | ws4.Cells(x + 3, 5).Value =\ 183 | f'=GETPIVOTDATA("Max of Total_Pwr(W)",Max_of_Max!R2C1,"TestCondition1","{row_header}")' 184 | else: 185 | ws4.Cells(x + 3, 4).Value =\ 186 | f'=GETPIVOTDATA("Max of Max_I_TC2(A)",Max_of_Max!R2C1,"TestCondition1","{test_points[x - 1]}")' 187 | ws4.Cells(x + 3, 3).Value =\ 188 | f'=GETPIVOTDATA("Avgerage of Max_I_TC2(A)",Average_of_Max!R5C1,"TestCondition1","{test_points[x - 1]}")' 189 | 190 | ws4.Cells(x + 3, 6).Value = f'=Sheet1!{tol_address}' 191 | ws4.Cells(x + 3, 7).Value = f'=if(D{x + 3}>F{x + 3},"Fail","Pass")' 192 | 193 | ws4.Activate() # set the worksheet with the EV report table as active 194 | 195 | """Format the Table""" 196 | """Merge and Center Max Total Power (W) column""" 197 | for x in range(ws3_first_data_row, ws3_last_data_row + 1, 2): 198 | ws4.Range(f'E{x}:E{x + 1}').Merge() 199 | ws4.Range(f'E{x}:E{x + 1}').HorizontalAlignment = win32c.xlCenter 200 | ws4.Range(f'E{x}:E{x + 1}').VerticalAlignment = win32c.xlCenter 201 | 202 | last_table_row = len(test_points) + 2 # because data begins on row 2 203 | 204 | """Format Numbers""" 205 | ws4.Range(f'C3:F{last_table_row}').Select() 206 | excel.Selection.NumberFormat = "0.000" 207 | 208 | """Autofit Width""" 209 | ws4.Columns('B:G').EntireColumn.AutoFit() 210 | 211 | """Conditional Formatting""" 212 | 213 | 'Dictionary values: Condition, Font Color, Fill Color' 214 | conditional_formatting = {'Pass': [-16752384, 13561798], 215 | 'Fail': [-16383844, 13551615]} 216 | 217 | for k, v in conditional_formatting.items(): 218 | ws4.Range(f'G3:G{last_table_row}').Select() 219 | excel.Selection.FormatConditions.Add(Type=win32c.xlTextString, TextOperator=win32c.xlContains, String=k) 220 | excel.Selection.FormatConditions(excel.Selection.FormatConditions.Count).SetFirstPriority() 221 | excel.Selection.FormatConditions(1).Font.Color = v[0] 222 | excel.Selection.FormatConditions(1).Interior.PatternColorIndex = win32c.xlAutomatic 223 | excel.Selection.FormatConditions(1).Interior.Color = v[1] 224 | 225 | """Set the Table Boarders""" 226 | border_types = {win32c.xlEdgeLeft: win32c.xlMedium, win32c.xlEdgeTop: win32c.xlMedium, 227 | win32c.xlEdgeBottom: win32c.xlMedium, 228 | win32c.xlEdgeRight: win32c.xlMedium, win32c.xlInsideVertical: win32c.xlThin, 229 | win32c.xlInsideHorizontal: win32c.xlThin} 230 | 231 | '''Boarders for entire table''' 232 | for k, v in border_types.items(): 233 | ws4.Range(f'B2:G{last_table_row}').Select() 234 | excel.Selection.Borders(k).Weight = v 235 | 236 | '''Boarders for top row''' 237 | for k, v in border_types.items(): 238 | ws4.Range(f'B2:G2').Select() 239 | excel.Selection.Borders(k).Weight = win32c.xlMedium 240 | 241 | 242 | if __name__ == "__main__": 243 | 244 | hostname, _ = get_host_name_ip() 245 | 246 | if hostname == 'INTREPID': 247 | f_path = r'd:\PythonProjects\Excel_Automation_with_Python\Pivot_Tables_and_Table' 248 | 249 | """Test Data""" 250 | f_name = 'pivot_tables.xlsx' 251 | 252 | run_excel(f_path, f_name) 253 | -------------------------------------------------------------------------------- /Pivot_Tables_and_Table/pivot_tables.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/Pivot_Tables_and_Table/pivot_tables.xlsx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![repo-image](excel_automation_repo_image.png) 2 | 3 | # Excel Automation with Python 4 | 5 | This repository contains a collection of scripts that automate workflows in existing Excel files using Python. The data in these examples is randomized, and the scripts are designed specifically for **Windows systems with Microsoft Excel installed**. 6 | 7 | ## Prerequisites 8 | 9 | - Python 3 (Legacy Python is **not supported**) 10 | - Windows OS 11 | - Microsoft Excel 12 | - [Anaconda (64-bit)](https://www.anaconda.com/download/) is recommended for managing Python environments and packages 13 | 14 | ## Why Automate Excel with Python? 15 | 16 | Many workplaces still rely heavily on Excel for data analysis and reporting. While Python libraries like Pandas offer more scalable and flexible solutions, integrating automation directly into Excel workflows can: 17 | 18 | - Improve productivity by reducing repetitive tasks 19 | - Preserve familiar Excel interfaces while adding automation 20 | - Allow use of native Excel formulas like `=MAX(A1:A30)` within automated processes 21 | 22 | ## Why Not Use Standard Excel Libraries? 23 | 24 | Popular Excel libraries include: 25 | 26 | - [`openpyxl`](https://openpyxl.readthedocs.io/en/stable/) 27 | - [`xlrd`](https://xlrd.readthedocs.io/en/latest/) 28 | - [`xlwt`](https://xlwt.readthedocs.io/) 29 | - [python-excel.org](http://www.python-excel.org/) 30 | 31 | While useful, these libraries have limitations: 32 | - Limited support for formulas and complex formatting 33 | - Inconsistent handling of existing Excel files 34 | - Not all features of Excel are accessible 35 | 36 | Instead, this project uses the `win32com` library to access Excel via the COM API: 37 | 38 | ```python 39 | import win32com.client as win32 40 | 41 | excel = win32.gencache.EnsureDispatch('Excel.Application') 42 | ``` 43 | 44 | This approach provides full control over the Excel application and allows direct manipulation of workbooks, formulas, and UI features. 45 | 46 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /excel_automation_repo_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trenton3983/Excel_Automation_with_Python/4e7e20765715c243d2aa9f9b283c60a79c9e4764/excel_automation_repo_image.png --------------------------------------------------------------------------------