Writing data to excel from a formula in python - python

What I want is with openpyxl to write a value I get form a len() or dups() to an excel cell.
Here are my imports:
import xlwings as xw
Here is the code:
#Load workbook
app = xw.App(visible = False)
wb = xw.Book(FilePath)
RawData_ws = wb.sheets['Raw Data']
Sheet1 = wb.sheets['Sheet 1']
RawData_ws['A1'] = (len(df.index))
Sheet1['B7'] = (len(df.index) - tot_dups))
RawData_ws['A2'] = (len(df.index)) #This one is after removing duplicate values
Tot_dups:
tot_dups = len(df.index)
I want the values of the different len() to show be written in the specific cells.

So, I already found the solution.
Change:
RawData_ws['A1'] = (len(df.index))
For:
RawData_ws['A1'].values = (len(df.index))

Related

is there a way to write in cells vertically via xlwings?

I want to organize the (split)ed result vertically like
this
not this
import xlwings as xw
wb = xw.Book()
ws = wb.sheets.active
result = "1,2,3,4,5,6,7"
ws.range(1,1).value = result.split(",")
The xlwings tutorial says:
To write a list in column orientation to Excel, use transpose:
sht.range('A1').options(transpose=True).value = [1,2,3,4]

Using Pandas and xlrd together. Ignoring absence/presence of column headers

I am hoping you can help me - I'm sure its likely a small thing to fix, when one knows how.
In my workshop, neither I nor my colleagues can make 'find and replace all' changes via the front-end of our database. The boss just denies us that level of access. If we need to make changes to dozens or perhaps hundreds of records it must all be done by copy-and-paste or similar means. Craziness.
I am trying to make a workaround to that with Python 2 and in particular libraries such as Pandas, pyautogui and xlrd.
I have researched serval StackOverflow threads and have managed thus far to write some code that works well at reading a given XL file .In production, this will be a file exported from a found data set in the database GUI front-end and will be just a single column of 'Article Numbers' for the items in the computer workshop. This will always have an Excel column header. E.g
ANR
51234
34567
12345
...
All the records numbers are 5 digit numbers.
We also have the means of scanning items with an IR scanner to a 'Workflow' app on the iPad we have and automatically making an XL file out of that list of scanned items.
The XL file here could look something similar to this.
56788
12345
89012
...
It differs in that there is no column header. All XL files have their data 'anchored' at cell A1 on 'Sheet1" and again just single column will be used. No unnecessary complications here!
Here is the script anyway. When it is fully working system arguments will be supplied to it. For now, let's pretend that we need to change records to have their 'RAM' value changed from
"2GB" to "2 GB".
import xlrd
import string
import re
import pandas as pd
field = "RAM"
value = "2 GB"
myFile = "/Users/me/folder/testArticles.xlsx"
df = pd.read_excel(myFile)
myRegex = "^[0-9]{5}$"
# data collection and putting into lists.
workbook = xlrd.open_workbook(myFile)
sheet = workbook.sheet_by_index(0)
data = [[sheet.cell_value(r, c) for c in range(sheet.ncols)] for r in range(sheet.nrows)]
formatted = []
deDuped = []
# removing any possible XL headers, setting all values to strings
# that look like five-digit ints, apply a regex to be sure.
for i in data:
cellValue = str(i)
cellValue = cellValue.translate(None, '\'[u]\'')
# remove the decimal point
# Searching for the header will cause a database front-end problem.
cellValue = cellValue[:-2]
cellValue = cellValue.translate(None, string.letters)
# making sure only valid article numbers get through
# blank rows etc can take a hike
if len(cellValue) != 0:
if re.match(myRegex, cellValue):
formatted.append(cellValue)
# weeding out any possilbe dupes.
for i in formatted:
if i not in deDuped:
deDuped.append(i)
#main code block
for i in deDuped:
#lots going on here involving pyauotgui
#making sure of no error running searches, checking for warnings, moving/tabbing around DB front-end etc
#if all goes to plan
#removing that record number from the excel file and saving the change
#so that if we run the script again for the same XL file
#we don't needlessly update an already OK record again.
df = df[~df['ANR'].astype(str).str.startswith(i)]
df.to_excel(myFile, index=False)
What I really would to like to find out is how can I run the script so that "doesn't care" about the presence or absence of the column header.
df = df[~df['ANR'].astype(str).str.startswith(i)]
Appears to be the line of code where this all hangs on. I've made several changes to the line in different combination but my script always crashes.
If a column header, ("ANR") in my case, is essential for this particular 'pandas' method is there a straight-forward way of inserting a column header into an XL file if it lacks one in the first place - i.e the XL files that come from the IR scanner and the 'Workflow' app on the iPad?
Thanks guys!
UPDATE
I've tried as suggested by Patrick implementing some code to check if cell "A1" has a header or not. Partial success. I can put "ANR" in cell A1 if its missing but I lose whatever was there in the first place.
import xlwt
from openpyxl import Workbook, load_workbook
from xlutils.copy import copy
import openpyxl
# data collection
workbook = xlrd.open_workbook(myFile)
sheet = workbook.sheet_by_index(0)
data = [[sheet.cell_value(r, c) for c in range(sheet.ncols)] for r in range(sheet.nrows)]
cell_a1 = sheet.cell_value(rowx=0, colx=0)
if cell_a1 == "ANR":
print "has header"
else:
wb = openpyxl.load_workbook(filename= myFile)
ws = wb['Sheet1']
ws['A1'] = "ANE"
wb.save(myFile)
#re-open XL file again etc etc.
I found this new block of code over at writing to existing workbook using xlwt. In this instance the contributor actually used openpyxl.
I think I got it fixed for myself.
Still a tiny bit messy but seems to be working. Added an 'if/else' clause to check the value of cell A1 and to take action accordingly. Found most of the code for this at how to append data using openpyxl python to excel file from a specified row? - using the suggestion for openpyxl
import pyperclip
import xlrd
import pyautogui
import string
import re
import os
import pandas as pd
import xlwt
from openpyxl import Workbook, load_workbook
from xlutils.copy import copy
field = "RAM"
value = "2 GB"
myFile = "/Users/me/testSerials.xlsx"
df = pd.read_excel(myFile)
myRegex = "^[0-9]{5}$"
# data collection
workbook = xlrd.open_workbook(myFile)
sheet = workbook.sheet_by_index(0)
data = [[sheet.cell_value(r, c) for c in range(sheet.ncols)] for r in range(sheet.nrows)]
cell_a1 = sheet.cell_value(rowx=0, colx=0)
if cell_a1 == "ANR":
print "has header"
else:
headers = ['ANR']
workbook_name = 'myFile'
wb = Workbook()
page = wb.active
# page.title = 'companies'
page.append(headers) # write the headers to the first line
workbook = xlrd.open_workbook(workbook_name)
sheet = workbook.sheet_by_index(0)
data = [[sheet.cell_value(r, c) for c in range(sheet.ncols)] for r in range(sheet.nrows)]
for records in data:
page.append(records)
wb.save(filename=workbook_name)
#then load the data all over again, this time with inserted header
workbook = xlrd.open_workbook(myFile)
sheet = workbook.sheet_by_index(0)
data = [[sheet.cell_value(r, c) for c in range(sheet.ncols)] for r in range(sheet.nrows)]
formatted = []
deDuped = []
# removing any possible XL headers, setting all values to strings that look like five-digit ints, apply a regex to be sure.
for i in data:
cellValue = str(i)
cellValue = cellValue.translate(None, '\'[u]\'')
# remove the decimal point
cellValue = cellValue[:-2]
# cellValue = cellValue.translate(None, ".0")
cellValue = cellValue.translate(None, string.letters)
# making sure any valid ANRs get through
if len(cellValue) != 0:
if re.match(myRegex, cellValue):
formatted.append(cellValue)
# ------------------------------------------
# weeding out any possilbe dupes.
for i in formatted:
if i not in deDuped:
deDuped.append(i)
# ref - https://stackoverflow.com/questions/48942743/python-pandas-to-remove-rows-in-excel
df = pd.read_excel(myFile)
print df
for i in deDuped:
#pyautogui code is run here...
#if all goes to plan update the XL file
df = df[~df['ANR'].astype(str).str.startswith(i)]
df.to_excel(myFile, index=False)

How to achieve something like VBA's "Range.Find" using win32?

I'm looking to achieve functionality similar to the Range.Find method in VBA using win32com package in Python. I'm dealing with an Excel CSV file. While I have found lots of solutions using range(), it seems to require specifying a fixed range of cells, as opposed to Range.Find in VBA, which will auto search in worksheet without fixing the range.
Here is my code:
import win32com.client as client
excel= client.dynamic.Dispatch("Excel.Application")
excel.visible= True
wb= excel.workbooks.open(r"ExcelFile.xls")
ws= wb.worksheets('First')
### This able to extract information:
test_range= ws.Range("A1")
### Got issue AttributeError: 'function' object has no attribute 'Find':
test_range= ws.Range.Find("Series ID")
print(test_range.value)
Does it mean Range.Find method does not supported in win32 package or I point it with the wrong existing module?
Bonus answer: if you are a fan of the Excel API (10x to #ashleedawg comment), you can use it directly through xlwings:
import xlwings as xw
bookName = r'C:\somePath\hello.xlsx'
sheetName = 'Sheet1'
wb = xw.Book(bookName)
sht = wb.sheets[sheetName]
myCell = wb.sheets[sheetName].api.UsedRange.Find('test')
print('---------------')
print (myCell.address)
input()
Thus an input like this:
Nicely returns this:
So with the first part of the code some Excel file with random-like numbers is generated:
import xlsxwriter
from xlsxwriter.utility import xl_rowcol_to_cell
import xlrd
#First part of the code, used only to create some Excel file with data
wbk = xlsxwriter.Workbook('hello.xlsx')
wks = wbk.add_worksheet()
i = -1
for x in range(1, 1000, 11):
i+=1
cella = xl_rowcol_to_cell(i, 0) #0,0 is A1!
cellb = xl_rowcol_to_cell(i, 1)
cellc = xl_rowcol_to_cell(i, 2)
#print (cella)
wks.write(cella,x)
wks.write(cellb,x*3)
wks.write(cellc,x*4.5)
myPath= r'C:\Desktop\hello.xlsx'
wbk.close()
#SecondPart of the code
for sh in xlrd.open_workbook(myPath).sheets():
for row in range(sh.nrows):
for col in range(sh.ncols):
myCell = sh.cell(row, col)
print(myCell)
if myCell.value == 300.0:
print('-----------')
print('Found!')
print(xl_rowcol_to_cell(row,col))
quit()
With the second part of the code, the real "Searching" starts. In this case, we are searching for 300, which is actually one of the generated values from the first part of the code:
So, python starts looping through rows and columns, comparing the values with 300. If the value is found, it writes Found and stops searching:
This code can be actually re-written, with making the second part as a function (def).
If you want to do it with a function, this is a way to do it - defCell is the name of the function.
import xlsxwriter
import os
import xlrd
import time
from xlsxwriter.utility import xl_rowcol_to_cell
def findCell(sh, searchedValue):
for row in range(sh.nrows):
for col in range(sh.ncols):
myCell = sh.cell(row, col)
if myCell.value == searchedValue:
return xl_rowcol_to_cell(row, col)
return -1
myName = 'hello.xlsx'
wbk = xlsxwriter.Workbook(myName)
wks = wbk.add_worksheet()
i = -1
for x in range(1, 1000, 11):
i+=1
cella = xl_rowcol_to_cell(i, 0) #0,0 is A1!
cellb = xl_rowcol_to_cell(i, 1)
cellc = xl_rowcol_to_cell(i, 2)
wks.write(cella,x)
wks.write(cellb,x*3)
wks.write(cellc,x*4.5)
myPath= os.getcwd()+"\\"+myName
searchedValue = 300
for sh in xlrd.open_workbook(myPath).sheets():
print(findCell(sh, searchedValue))
input('Press ENTER to exit')
It produces this after running it:
Yes win32com can do the exact same range.find() function. The problem with your code is you didnt specify what is the range. Range has no Find attribute.
test_range= ws.Range.Find("Series ID") #<-----no range specified
Below is the correct use of Range and Find
import win32com.client as client
excel= client.dynamic.Dispatch("Excel.Application")
excel.visible= True
wb= excel.workbooks.open(r"ExcelFile.xls")
ws= wb.worksheets('First')
test_range= ws.Range("A1")
### example if you want to find out the column of search result
ResultColumn= test_range.Find("Series ID").Column
print(str(ResultColumn))

Get the absolute value of a sum in an Excel sheet using openpyxl

I am starting to use openpyxl and I want to copy the sum of a row.
In Excel the value is 150, but when I try to print it, the output I get is the formula, not the actual value:
=SUM(B1:B19)
The script I use is:
print(ws["B20"].value)
Using "data_only" didn't work.
wb = ("First_File_b.xlsx" , data_only=True)
Any idea how I can solve to obtain the numerical value? Help would be greatly appreciated.
Okay, here's a simple example
I have created a spreadsheet with first spreadsheet "Feuil1" (french version) which contains A1,...,A7 as 1,2,3,4,5,6,7 and A8=SUM(A1:A7)
Here's the code, that could be adapted maybe to other operators. maybe not so simply. It also supports ranges from A1:B12 for instance, untested and no parsing support for cols like AA although could be done.
import openpyxl,re
fre = re.compile(r"=(\w+)\((\w+):(\w+)\)$")
cre = re.compile(r"([A-Z]+)(\d+)")
def the_sum(a,b):
return a+b
d=dict()
d["SUM"] = the_sum
def get_evaluated_value(w,sheet_name,cell_name):
result = w[sheet_name][cell_name].value
if isinstance(result,int) or isinstance(result,float):
pass
else:
m = fre.match(result)
if m:
g = m.groups()
operator=d[g[0]] # ATM only sum is supported
# compute range
mc1 = cre.match(g[1])
mc2 = cre.match(g[2])
start_col = ord(mc1.group(1))
end_col = ord(mc2.group(1))
start_row = int(mc1.group(2))
end_row = int(mc2.group(2))
result = 0
for i in range(start_col,end_col+1):
for j in range(start_row,end_row+1):
c = chr(i)+str(j)
result = operator(result,w["Feuil1"][c].value)
return result
w = openpyxl.load_workbook(r"C:\Users\dartypc\Desktop\test.xlsx")
print(get_evaluated_value(w,"Feuil1","A2"))
print(get_evaluated_value(w,"Feuil1","A8"))
output:
2
28
yay!
I have solved the matter using a combination of openpyxl and pandas:
import pandas as pd
import openpyxl
from openpyxl import Workbook , load_workbook
source_file = "Test.xlsx"
# write to file
wb = load_workbook (source_file)
ws = wb.active
ws.title = "hello world"
ws.append ([10,10])
wb.save(source_file)
# read from file
df = pd.read_excel(source_file)
sum_jan = df ["Jan"].sum()
print (sum_jan)

Formatting in OpenPyxl with use_iterators = True

This is similar to my earlier question getting formating data in openpyxl The only real difference is that now I really want to use the optimized workbook for the speed increase.
Basically I can't figure out how to retrieve formatting details when I use the optimized reader. Here's a toy sample, the comments explain what I'm seeing on the print statements. Am I doing something wrong? Is there a better way to retrieve formatting details?
Also, if anyone knows of a different excel reader for python that supports xlsx + retrieving formatting I'm open to changing! (I've already tried xlrd, and while that does support xlsx in the newer builds it doesn't yet support formatting)
from openpyxl import Workbook
from openpyxl.reader.excel import load_workbook
from openpyxl.style import Color, Fill
#this is all setup
wb = Workbook()
dest_filename = 'c:\\temp\\test.xlsx'
ws = wb.worksheets[0]
ws.title = 'test'
ws.cell('A1').value = 'foo'
ws.cell('A1').style.font.bold = True
ws.cell('B1').value = 'bar'
ws.cell('B1').style.fill.fill_type = Fill.FILL_SOLID
ws.cell('B1').style.fill.start_color.index = Color.YELLOW
wb.save(filename = dest_filename )
#setup complete
book = load_workbook( filename = dest_filename, use_iterators = True )
sheet = book.get_sheet_by_name('test')
for row in sheet.iter_rows():
for cell in row:
print cell.coordinate
print cell.internal_value
print cell.style_id #returns different numbers here (1, and 2 in case anyone is interested)
print sheet.get_style(cell.coordinate).font.bold #returns False for both
print sheet.get_style(cell.coordinate).fill.fill_type #returns none for bothe
print sheet.get_style(cell.coordinate).fill.start_color.index #returns FFFFFFFF (white I believe) for both
print
import openpyxl
print openpyxl.__version__ #returns 1.6.2
The style_ID appears to be the index for where you can find the style information in workbook -> shared_styles (book.shared_styles or sheet.parent.shared_styles).
In some workbooks this works flawlessly; but, I've also found in other workbooks the style_ID is greater than the length of shared_styles giving me "Out of range" Exceptions when I try to access said styles.

Categories

Resources