Auto expand table range in excel - python

Tying to find to an Excel VBA equivalent to
sheet.range('A1').expand('table')
#https://docs.xlwings.org/en/stable/datastructures.html
I've tried to create a xlwings func like this :
#xw.func
def expand(rng, caller):
sht = caller.sheet
return sht.range(rng).expand().address
=expand("C7") returns "$C$7:$E$8" (works)
So I've tried for feed this rng as string inside the following macro (that spots changes within a range)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng_s As String
rng_s = expand("C7") #This is where there is the error
Set rng = Target.Worksheet.Range(rng_s)
If Not Intersect(Target, rng) Is Nothing Then my_macro2 (rng)
End Sub
#The Python console returns : TypeError: The Python instance can not be converted to a COM object
Any idea how to make this table expand automatically? Or make this xlwings function work?

You need to call the function directly instead of using xlwings way as they need to correct the code in order to support calling from another function/sub.
Use this in your function instead of calling expand
rng_s = Py.CallUDF("udftest", "expand", Array("C7", Nothing), ThisWorkbook, ThisWorkbook.ActiveSheet.Range("C7"))
Change ThisWorkbook.ActiveSheet for the Sheet where the range you are looking for belongs, that last parameter is passed as the caller in the defined python function, so even you could use only that parameter do get the desired range in your expand function.

Related

Can you make an instanced object accessing an attribute be variable?

Consider my following function. The argument takes in a string or int, and it compares the input to each excel cell value, starting from the first row and goes to the rightmost column and then going down to the last row. If a cell matches with the argument, then it returns information about the position of cell. If there is more than one match, than it continues to append the triplet to the information list.
My question is, is it possible to make an object instance, an argument of the function? Because in my function, the object, "ws", that is being dotted with an attribute or method "iter_rows" is static. So everytime I call this function, it will only access "ws". I would like it if there was a way to change the object when you are calling the function. By perhaps adding a second argument to the function and making the instantiated object a "variable". Though from my testing, it seems like this is not possible. I am not sure if what I'm trying to do is not allowed in general or it's just this openpyxl class. The obvious naive alternative solution is making a separate function called something like "locate2" and changing "ws" to "ws1". Is there any way to implement this without creating another function and hard coding the instances?
def locate(val):
position_info = []
for row_s in ws.iter_rows(min_col=1, max_col=ws.max_column):
for cell_s in row_s:
if cell_s.value == val:
position_info.append([cell_s.row, cell_s.column, cell_s.coordinate])
if not position_info:
print("There is no cell with this value.")
else:
return position_info
path = 'C:\test.xlsx'
path2 = 'C:\test1.xlsx'
wb = xl.load_workbook(path, data_only=True)
ws = wb.worksheets[0]
wb1 = xl.load_workbook(path2, data_only=True)
ws1 = wb1.worksheets[0]
print(locate("hello"))
I tried to make the object a variable so I can choose what the object can be. But I kept getting attribute error.

Use eval function with 'mean()' and 'median()'

I want to calculate mean and median from of a dataframe so I put them in a list as follows:
comb_methods = ['median','mean']
I use a loop and use eval function to make the functions callable, and calculate the result and add it as a new column to the dataframe
for combin in comb_methods:
combination = eval(combin)
heartdata[combin] = heartdata.combination(axis=1)
I get the following error.
name 'median' is not defined
I'm trying to understand why this is occurring for hours but I can't figure it out!
You need to use getattr instead of eval:
for combin in comb_methods:
heartdata[combin] = getattr(heartdata, combin)(axis=1)
getattr looks for the attribute of a given object with a name as a string. Writing
getattr(heartdata, 'median')
returns heartdata.median (a method which we then call with the axis=1 argument).
eval on the other hand simply evaluates whatever string you pass onto it. So
eval('median')
is the same as simply typing median (without quotes) on a Python script. Python will believe that median is a variable, and will throw the error you see when it can't find said variable.

Using a Variable in a .query function

I'm trying to create a function that takes the input name of a value in a column and that value will then be used in a df.query function. However, I cannot figure out how to make it a variable that it recognizes as the input.
This is what I have right now:
def gettingWeeks(stateAbbr, stateName):
stateCases = cases.query('state == stateName')
But it does not recognize stateName. Is there a way to do this?
Thanks!
Pandas DataFrame.query method expects an expression string created accordingly to its specific syntax. To use variables from the current name space you have to use # symbol before the name of the variable:
stateCases = cases.query("state == #stateName")
Should work fine.
Here is the doc.

Get entire function as string / and transform string into function?

I want to get an entire Python function and save it as string (for instance, Javascript can simply do functionString = functionVar.toString().
Example:
#I defined a function with a body:
def someFunction(hey):
return hey + hey + hey
I want to convert this function to string and get exactly the same text as I typed above (without the comment, of course)
Then I want to do the inverse operation: from the string, convert it to a function and and store it in a variable to be called.
Using exec you can almost get what you're asking. Encoding the function as a string is the easy part.
"""Encode the function as a string"""
import inspect
funcString = inspect.getsource(someFunction)
Retrieving the function from the string is a touch more work. The following approach will have issues with globals or other variables outside the scope of the function you're trying to recover, but if the source completely specifies it then the following idea should work.
d = {}
exec(funcString, d)
f = next(d[k] for k in d if k != '__builtins__')
After running, the code defined by the source used to generate f in the first place will be bound to the variable f.

re-naming data frame output given input parameter to class in python

I have managed to make my first class, containing two methods where on holds data and the other does a set of derivations on the input data, given fairly simple python code utilizing Pandas functionality. I call the class, and its adherent methods, with 4 parameters.
What I would like is for the output from the class, a Pandas data frame, to be named according to one of the input parameters - being an string object. This since the derivation is made on a single data column in the input data and it would be practical to name the output data frame to the same as the input parameter (i.e., data frame variable) for later use.
An crude example of the structure of the class (and of the code) is give below:
class tool_swoe_iv:
"docstring"
def __init__(self,data_in):
self.data_in = data_in
def swoe_iv(self, param1, param2):
"code that executes calculations"
# output table which I would like to be re-named to param1 so it can be called later on.
woe_holder
When I call the class I would like the "woe_holder" data frame to have the same name as param1. Does my questions make sense? I come from a SAS background where the re-naming of the output data could easily have been done by a macro variable holding the name suffix.
Please note that im very new to the more formal parts of python coding involving classes and their development, honestly also to more standard python coding too.
Best,
I think that you're misunderstanding how python class methods work. Here's what I think you believe your code will do:
#Create an instance of tool_swoe_iv
my_tool = tool_swoe_iv(5)
#Execute calculations
my_tool.swoe_iv(param1, param2)
#Now there exists a new dataframe called param1
What you're missing here is that python functions return values. For example:
def addition(x, y):
return x + y
Now, if you want to have a variable that holds the result of an addition, you should assign it yourself, e.g.:
my_var = addition(1, 2)
Instead of doing it the way you're suggesting, with some function that looks like:
addition("myvar", 1, 2)
So using your original example, your code should look something like
class tool_swoe_iv:
"docstring"
def __init__(self,data_in):
self.data_in = data_in
def swoe_iv(self, param2):
"code that executes calculations"
# output table which I would like to be re-named to param1 so it can be called later on.
return dataframe
my_tool = tool_swoe_iv(5)
param1 = my_tool.swoe_iv(param2)
You can directly modify dictionary of variables by calling locals() and globals() function. For example, a = 1 is equal to locals()['a'] = 1.
So, you can use those functions to accomplish your idea:
globals()[param1] = woe_holder
However, this is not suggested, because you will assign it to a global name instead of local name. (If you use locals(), it will disappear when your function returns, because it will be a local variable of your function.) You'd better just return woe_holder to the caller and let the caller decide what name to assign to. For example, call my_tool.swoe_iv like that:
param1 = my_tool.swoe_iv(param2)
(In this case param1 can't be a object. Don't enclose it with quotation marks.)
This is more readable.

Categories