I was also wondering how to achieve this. The above just makes the Excel cell literally contain the string in question (e.g. "#N/A"), so it appears to have worked but doesn't actually behave the same way as a proper #N/A. For example, a proper #N/A multiplied with a number gives another #N/A, but a string (like in this case) multiplied by a number gives #VALUE (etc).
When a proper #N/A is input to xlwings it becomes a float (-2146826246), but returning this value from xlwings also literally just makes the Excel cell contain that number. Maybe because some conversion gets in the way.
import xlwings as xw
sheet = xw.books.active.sheets[0]
sheet.range("A5").formula = '#N/A'
sheet.range("A6").formula = '#DIV/0!'
sheet.range("A7").formula = '#NAME?'
sheet.range("A8").formula = '#NULL!'
sheet.range("A9").formula = '#NUM!'
sheet.range("A10").formula = '#REF!'
sheet.range("A11").formula = '#VALUE!'
When drawing charts using data return by UDF, it's friendly to see #N/A. (chart on the right is what I expect.)
# UDF
@xw.func
@xw.ret(expand='table')
@xw.ret(index=False)
@xw.ret(header=False)
@xw.ret(empty='NA')
def test():
df = pd.DataFrame([[1,3,None,2,1]])
return df
But we can not manipulate value in array return by UDF. Is there any other ways?
You can't properly return #N/A
via UDFs currently, but in your case it might be better to just leave away the @xw.ret(empty='NA')
.
Thanks very much. My temporary solution is to set up another excel native function to format UDFs data.
@fzumstein do you have already an idea on how to fix this (variant representing an error)?
I guess it falls in the C++ domain of the addin...
@fzumstein when I put a breakpoint on that line, I never reach it when running a python function from excel.
I would like to be able to introspect what is sent at COM level to see what the conversion from python to COM variant is doing (if this makes sense)
hi @fzumstein , based on an idea in https://stackoverflow.com/questions/45017673/how-to-filter-an-array-with-a-worksheet-function, I came up with the function ReplaceEmpty
Private arr_()
Public Function Values() As Variant()
Values = arr_
End Function
Public Function ReplaceEmpty(arr(), expression As String) As Variant()
arr_ = arr
ReplaceEmpty = Application.Evaluate("IF(Values()=""""," & expression & ",Values())")
End Function
that can then wrap the output to replace the empty values by N/A
Py.CallUDF("the_module", "the_function", Array(param), ThisWorkbook, Application.Caller)
ReplaceEmpty(Py.CallUDF("the_module", "the_function", Array(param), ThisWorkbook, Application.Caller), "#N/A")
A bit of a hack (specially with the global variable arr_ and potential reentrancy issues) but could give you ideas for a clean fix
Hi @sdementen, thanks! Actually, you are right about the previous comment, but the conversion is still handled in pywin32 and from what I figure, while it delivers the different error types as integers when reading, it doesn't support them when writing so your solution sounds like the sensible way of doing it!
And what do you think of the concurrency risk on the global variable ?
On Mon, Nov 15, 2021, 15:24 Felix Zumstein ***@***.***> wrote:
Hi
@sdementen <
https://github.com/sdementen>, thanks! Actually, you are
right about the previous comment, but the conversion is still handled in
pywin32 and from what I figure, while it delivers the different error types
as integers when reading, it doesn't support them when writing so your
solution sounds like the sensible way of doing it!
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<
#776 (comment)>,
or unsubscribe
<
https://github.com/notifications/unsubscribe-auth/AAJ6S5VWWJIQA4LKUJFJVXLUMEJYBANCNFSM4ECCJJ5A>
Triage notifications on the go with GitHub Mobile for iOS
<
https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<
https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
Isn't Excel using sometimes multiple threads when running calculations?
If so, there maybe a need to have a global dict in Excel indexés by the
caller cell to avoid concurrency issues.
BTW, I succeeded once to return a real N/A from python when returning an
array with some cell being the large integer representing N/A. But I could
not reproduce it :-(
That's why I was interested in the piece of code that wraps the returning
values into Variant (I haven't found it yet)
On Tue, Nov 16, 2021, 09:19 Felix Zumstein ***@***.***> wrote:
I don't think VBA has a concurrency issue?
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<
#776 (comment)>,
or unsubscribe
<
https://github.com/notifications/unsubscribe-auth/AAJ6S5VXLUNOSM6WHCQO6FTUMIHZFANCNFSM4ECCJJ5A>
Triage notifications on the go with GitHub Mobile for iOS
<
https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<
https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
Yes, but only with built-in functions and external libraries, not with VBA AFAIK. I would have expected the following to work if it was supported, but it doesn't:
import xlwings as xw
import pythoncom
from win32com.client import VARIANT
@xw.func
def hello(name):
return VARIANT(pythoncom.VT_ERROR, -2146826246)
Maybe you can ask on https://mail.python.org/mailman/listinfo/python-win32 ?
excel = Dispatch("Excel.Application")
wb = excel.ActiveWorkbook
wb.ActiveSheet.Range("A1:C1").Value = ["#N/A", 1, 2]
writes indeed a real #N/A in A1.
Yet when xlwings returns the same array, the #N/A is a string.
So not sure what to ask on python-win32 mailing list...
Adjusting my previous comment for https://docs.microsoft.com/en-us/office/vba/excel/concepts/cells-and-ranges/cell-error-values, I'd actually would expect the following to work (but it doesn't):
import xlwings as xw
import pythoncom
from win32com.client import VARIANT
@xw.func
def hello(name):
return VARIANT(pythoncom.VT_ERROR, 2042)
So the question would probably be if there's an equivalent of CVErr(xlErrNA)
in pywin32 given that VARIANT(pythoncom.VT_ERROR, 2042)
doesn't seem to work.
NA/nan within returned DataFrame via UDF back to Python is parsed to empty string '' instead of NA/nan
#1947
@talalvas could you show an example how to implement xloil.CellError.NA
?
You have to use the xloil library for that.
import xloil as xlo
@xlo.func
def xlo_df_rows(df):
if type(df) is xlo.CellError:
# lookup_df is #NULL! (empty dataframe)
if df == xlo.CellError.NULL:
return 0
return len(df.index)
You can change the NULL to ant error supported in the CellError class.