How to insert array formula in an Excel sheet with openpyxl? - python

I'm using OpenPyxl to create and modify an Excel sheet.
I have the following formula in Excel:
=(SUM(IF(LEFT(Balances!$B$2:$B$100,LEN($B4))=$B4,Balances!$D$2:$D$100)))
This formula which is an "array formula" is working but in order to write it by hand, I have to finish with CTRL+SHIFT+ENTER (because it's an array formula).
This transform then the formula as follow:
{=(SUM(IF(LEFT(Balances!$B$2:$B$100,LEN($B4))=$B4,Balances!$D$2:$D$100)))}
I want to be able to write this formula via OpenPyxl with the following code:
sheet.cell(row=j, column=i).value = '{=(SUM(IF(LEFT(Balances!$B$2:$B$100,LEN($B4))=$B4,Balances!$D$2:$D$100)))}'
However, it doesn't work. OpenPyxl can't manage it. It give me the formula written but not working.
I could do it with XLSX Writer
https://xlsxwriter.readthedocs.io/example_array_formula.html
However XLSX writer doesn't work with already created files.
I don't see which path to follow.

Use the worksheet.formula_attributes to set the array formula. Place the formula in the desired cell, A1 for this example. Then set the formula_attributes to the cell range you want to apply the formula to.
ws["A1"] = "=B4:B8"
ws.formula_attributes['A1'] = {'t': 'array', 'ref': "A1:A5"}

In case solution provided above does not work, check whether you are using english name of functions in your formulae.
In my case I have been using czech function name and although formulae works if inserted manually, it did not work when inserted via openpyxl.
Switching to english name of the function solved the issue!

In my case the formula was using arrays for intermediate results before summarizing with a MAX. The formula worked OK when typed in but not when inserted via openpyxl. Office 365 version of Excel was inserting the new implicit intersection operator, #, incorrectly.
formula: ="Y" & MAX(tbl_mcare_opt[Year]*(tbl_mcare_opt[Who]=[#Who])*(tbl_mcare_opt[Year]<=intyear(this_col_name())))
It turns out that the properties needed to be set, as above. This allowed Excel to correctly interpret the formula. In my case the ref turned out to be just the single cell address.
I was able to determine that the formula was using dynamic arrays with a regex. If it was then I added the formula properties.
# provision for dynamic arrays to be included in formulas - notify excel
if is_formula(values[cn]):
regex_column=r'[A-Za-z_]+(\[\[?[ A-Za-z0-9]+\]?\])'
pattern=re.compile(regex_column)
matches=pattern.findall(values[cn])
if len(matches): # looks like a dynamic formula
address=get_column_letter(cix)+str(rix)
ws.formula_attributes[address]={'t':'array','ref': address}

Related

Cannot write formula excel to cell with openpyxl

i am creating interactive excel, and i am using openpyxl library. For some reason excel cannot be loaded according to error with formula. There is how i put formula in cell:
ws = self.wb.create_sheet('help')
ws['C6'] = '=IFERROR(FILTER(other_sheet!B3:B12; other_sheet!D3:D12=main_sheet!C3; "No results"); "error")'
Formula work, when I directly pass them to excel, also the way i did is correct. Basic formulas instead of FILTER works (eg. sum). I assumed that this formula didnt work with openpyxl. And i should use built-in function in openpyxl but imo it won't work dynamically (eg. this statement: other_sheet!D3:D12=main_sheet!C3)
I saw that in manual is obligatory to use comas, so i applied it. But it still doesnt work
ws['C6'] = '=FILTER(other_sheet!B3:B12, other_sheet!D3:D12=main_sheet!C3, "No results")'
But it doesnt change anything, problem still appears. Any suggestions?
I also tried with ws['C6'].value

Make Excel Cell value Variable in Python Using Pandas

I have looked for a while on this one but can't seem to find out how to pick a specific cell value in an excel worksheet and assign it to variable in python. I get a Traceback Error with the code below.
I have a number of work rules I want to assign as variables in python that are stored in an cells within an excel workhseet.
(work rules[4][2] is how I am trying to make the cell value into a variable.
Code:
work_rules = pd.read_excel(
'D:\\Personal Files\\Technical Development\\PycharmProjects\\Call Center Headcount Model\\Call Center Work Rules.xlsx',
sheet_name='Inputs')
historical_start_date = work_rules[4][2]
print(historical_start_date)
Found it:
Use the iloc method on the excel object: work_rules.iloc(4, 2)

How to copy a formula from one gsheet to another using python?

I'm not sure if this is possible. I have tons of spreadsheet, and the formulas need to be updated. How do I copy a formula from one cell or a group of cells to another? I've used gspread and it seems it can only do values. I need python to basically paste formulas on hundreds of sheets for me, without me opening each individually and copy and pasting the formulas.
Does anybody have a generic solution for copying and pasting formulas? This is pretty important, you would think someone can do it.
Update 19 July 2018:
Here's how you do it:
# Get the formula value from the souce cell:
formula = wks.acell('A2', value_render_option='FORMULA').value
# Update the target cell with formula:
wks.update_acell('B3', formula)
(this works since gspread 3.0.0 which uses Sheets API v4.)
Original answer below:
To access a formula value, you need to use the input_value attribute of a cell object.
Here's an example. (I'm skipping the initialization step. Let's assume you've already opened a spreadsheet and wks is referring to you worksheet object.)
# To copy a formula from a single cell (say, A2) to another cell (B3)
# use the input_value property
formula = wks.acell('A2').input_value
# then paste the value to a new cell
wks.update_acell('B3', formula)
For a group of cells, you'd want to use a wks.range() method to get cell objects. Then you can get formula values via input_value as in the code above. See the example on the GitHub.

How to Check the cell from excel which has formula using python

I Want to access the cell which has formula from the excel workbook.Actually my python script is working fine only to read the data from excel, but i need that only the cell which has formula and to print that cells only
An example would be really appreciated..........
Two things that could help you solve the problem:
There's a typo: HasForumla instead of HasFormula
e is a string, not a cell object, so e.HasFormula won't work.
You probably want to use
e = sheet.cell(row,17)
to access the Cell object at that position. I'm not sure where you got your HasFormula attribute from, though - couldn't find it in the docs.
Edit:
I just looked at the README for the current distribution where it's stated that
xlrd will safely and reliably ignore any of these if present in the
file:
[...]
Formulas (results of formula calculations are extracted, of course).
[...]

Setting criteria on an autofilter in pyWin32

I can set up an autofilter using pyWin32, but I wondered if it's possible to set a default filter and what the syntax would be.
For example, I'd like to set a filter on a year column and set the default for the current year.
xl = Dispatch("Excel.Application")
xl.Workbooks.Open(file_path)
xl.ActiveWorkbook.Worksheets(sheetname).Range("A2:A6").AutoFilter(1)
xl.ActiveWorkbook.Close(SaveChanges=1)
I've looked on the web for documentation on pywin32, and also Microsofts site, but can't work out how to translate the MS syntax to pywin32
Range("A2:A6").AutoFilter Field:=1, Criteria1:=rng.Value
I bumped into the same problem and after a bit of experimentation, I found that it was possible to set a range on the Columns attribute. Since I wanted to autofilter on columns A thru I, I set the criteria as follows:
xl.ActiveWorkbook.ActiveSheet.Columns("A:I").AutoFilter(1)
This worked for me. I'm assuming that you want to filter on Columns B thru F since AutoFilter is enabled only for columns. Perhaps the following criteria will work for you:
xl.ActiveWorkbook.ActiveSheet.Columns("B:F").AutoFilter(1)
Alok
The rather cryptic documentation is available at: http://msdn.microsoft.com/en-us/library/office/bb242013(v=office.12).aspx.
Each of the Excel VBA parameters translates to a function argument in pywin32. For example, if you want to filter all years that aren't equal to "2012" you would do this by specifying the Criteria1 parameter as follows:
MyYearRange.AutoFilter(Field=1, Criteria1="2012")
I'm just throwing an answer here for future people who want to use a different but similar solution. It is a lot more simple though. You will need to install xlwings and have pywin32. With xlwings, you can access the api functions of the pywin32 giving you a lot of flexibility on top of its own functions.
import xlwings
#puts the excel window into focus or opens it up. It evens work on csv files.
wb = xlwings.Book('C:\\Users\\yourusername\\Desktop\\Excel.xlsx')
#Choose the sheet you want to focus
datasht = wb.sheets['Sheet1']
#Pay attention to where you the .api. part. It matters if you are trying to achieve something specific. AND MAKE SURE to that you follow case-sensensitive typing for 'Range' and 'Autofilter'.
datasht.api.Range('A1:J10').AutoFilter(3,'SomeFilterValue')
Unfortunately, I'm not sure how to bring about the rest of the arguments. You pretty much just have to figure out how to translate the arguments into python. I did get it to work, but I'm unsure if you would run into any issues. here is one that would work
datasht.api.Range('A1:J10').AutoFilter(3,'filtervalue1',2,'filtervalue1',1)
Read the 2nd link specifically if you need to call on the Operator Parameter:
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-autofilter-method-excel
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/xlautofilteroperator-enumeration-excel
If you need to select multiple filter values in the same column:
ws.Columns('ColumnLetter:ColumnLetter').AutoFilter(column_number, value_list, 7)
From https://learn.microsoft.com/en-us/office/vba/api/excel.xlautofilteroperator:
xlFilterValues | 7 | Filter values
This works:
Excel = win.Dispatch("Excel.Application")
Excel.visible = True
wb = Excel.Workbooks.open('path to xlsx')
ws = wb.Worksheets(1)
#use Range("A:A") for autofilter
ws.Columns("A:I").AutoFilter(1,"criteria string")
This will apply AutoFilter on Column A with Criteria1 is "criteria string"

Categories