Pandas version checks
-
[X] I have checked that this issue has not already been reported.
-
[X] I have confirmed this bug exists on the latest version of pandas.
-
[X] I have confirmed this bug exists on the main branch of pandas.
Reproducible Example
import pandas as pd
# To demonstrate the bug we need to load the pint library
from pint import Quantity as Q_
# Create a two-element DataFrame with Quanities
qq = pd.DataFrame({'qq': [Q_(1.0,'m'), Q_(2.0, 'm')]})
print(qq)
qq
0 1.0 meter
1 2.0 meter
# Attempt to replace the first element with a new Quantity
qq.iloc[0, 0] = Q_(3.0,'m')
# This will fail with TypeError: object of type 'float' has no len()
Issue Description
The issue concerns a change made 9 months ago: https://github.com/pandas-dev/pandas/commit/63ae9aac0555009b2e83bdb7a764d51c72929c90#diff-55001624a0932c1b6cee2e6ddb65dea85c1faf0dee84812c0ca0c32916a71438
Previously, the conditional logic sought to test whether the right hand side was scalar or not, which had one problem. It was changed to use lib.is_list_like
, which creates the problem (actually two problems) we see today.
I have also discussed with the Pint people here: https://github.com/hgrecco/pint/discussions/1617 It's quite possible to real problem is with the cython code implementing is_list_like
, which does not obey the Python standard for checking whether something is iterable (and therefore list-like).
Expected Behavior
The expected behavior is that Pandas would replace the first element of the DataFrame with the new value, resulting in the following DataFrame:
qq
0 3.0 meter
1 2.0 meter
Because pandas/core/dtypes/interence.py implements is_list_like
as follows:
is_list_like = lib.is_list_like
It should be possible for me to work around this as follows:
from pandas.core.dtypes.inference import is_list_like
from pandas._libs import lib
def new_is_list_like(x):
if lib.is_list_like(x):
try:
iter(x)
return True
except TypeError:
return False
return False
Here you can see the specific confusion of treating a scalar Pint quantity as list-like:
>>> is_list_like(Q_(1, 'm'))
True
>>> new_is_list_like(Q_(1, 'm'))
False
>>> is_list_like = new_is_list_like
>>> is_list_like(Q_(1, 'm'))
False
>>> qq.iloc[0, 0] = Q_(3.0,'m')
That last line should have given me the expected behavior, but the patch calls lib.is_list_like
instead of is_list_like
, defeating the work-around.
Installed Versions
Comment From: jbrockmendel
"still" suggests this has been discussed before. is there another issue for this?
Comment From: andrewgsavage
On latest pint-pandas this is not an issue (it may not have before). You need to specify pint dtype to make this work.
``` import pandas as pd import pint import pint_pandas from pint import Quantity as Q_
qq = pd.DataFrame({'qq': [Q_(1.0,'m'), Q_(2.0, 'm')]}, dtype ="pint[m]") print(qq)
qq
0 1.0 1 2.0
# Attempt to replace the first element with a new Quantity qq.iloc[0, 0] = Q_(3.0,'m') print(qq)
qq
0 3.0 1 2.0```
Comment From: topper-123
I just tried this and it works as expected. Thanks for the report, @MichaelTiemannOSC