mrkn / pycall.rb

Calling Python functions from the Ruby language
MIT License
1.05k stars 72 forks source link

How to use PyCall.exec/eval ? #133

Closed dsounded closed 3 years ago

dsounded commented 3 years ago

t["New Date"] = pd.to_datetime(t["DATE"]) - np.timedelta64(1,"D")

I have a code which looks like this. and it fails with unsupported operand type(s) for -: 'DatetimeArray' and 'type'

Instead of converting the types I'd like to eval such examples on python side. e.g.

t["New Date"] = PyCall.exec('pd.to_datetime(t["DATE"]) - np.timedelta64(1,"D")')

but I got this <class 'NameError'>: name 'pd' is not defined

I also tried

t["New Date"] = PyCall.exec('pd.to_datetime(t["DATE"]) - np.timedelta64(1,"D")', locals {pd: pd, np: np}) and got

<class 'TypeError'>: exec() takes no keyword arguments

Tried without keywords but passing nil and hash, since there is no specs for globals/locals within eval it's hard to navigate proper usage...

This works in python like: t["New Date"] = pd.to_datetime(t["DATE"]) - np.timedelta64(1,"D")

Could you please help ?

mrkn commented 3 years ago

I guess your problem is not how to use PyCall.exec. Your problem is why np.timedelta64(1, "D") turns to be a type.

Do you know that np.timedelta64(1, "D") is a syntax sugar of np.timedelta64.__call__(1, "D")? In Python, every object that has __call__ attribute can be called by obj(...) form. And, every type object in Python has __call__ attribute. That is the constructor of the type.

In Python, you can call a type object by type(..) form, but you cannot in Ruby. This difference comes from the difference between lisp-1 and lisp-2 semantics.

PyCall provides a new class method in every wrapper object of Python types. You can emulate np.timedelta64(1, "D") in Python by np.timedelta64.new(1, "D") in Ruby.

Or, PyCall binds obj.() to obj.__call__(), so you can also write np.timedelta64.(1, "D").

dsounded commented 3 years ago

Thanks!