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.
-
[ ] I have confirmed this bug exists on the main branch of pandas.
Reproducible Example
import numpy as np
import pandas as pd
N = 10
hash_dtype = np.dtype([(f'h{i}', np.uint64) for i in range(4)])
hashes = np.zeros(N, dtype=hash_dtype)
df = pd.DataFrame(data={'hashes':hashes, 'other_stuff':np.zeros(N)})
idx = [0, 1]
## This fails
df.loc[idx, 'hashes'] = np.ones(len(idx), dtype=hash_dtype)
# LossySetitemError:
# During handling of the above exception, another exception occurred:
# AssertionError
# AssertionError: Something has gone wrong, please report a bug at https://github.com/pandas-dev/pandas/issues
## This also files
print(df)
# TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
## This is ok
print(df['other_stuff'])
## This is a partial workaround
df['hashes'] = np.zeros(len(df), dtype=hash_dtype) # same errors with or without this line
df['hashes'].values[idx] = np.ones(len(idx), dtype=hash_dtype)
## But things still broken
print(df['hashes'])
# TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
## This works
print(df.loc[0, 'hashes'])
## This doesn't
print(df.loc[:, 'hashes'])
# TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
## OK
x = df['hashes']
## Not OK
x = df[['hashes', 'other_stuff']]
# TypeError: void() takes at least 1 positional argument (0 given)
Issue Description
Working with a numpy array with a structured type consisting of four 64 bit integers, there are various errors when getting/setting the associated data in a dataframe.
- Trying to set values with
df.loc[idx, 'hashes'] =
get LossySetitemError, AssertionError: Something has gone wrong - can work-around by directly indexing into the underlying array
df['hashes'].values[idx] =
(but for other indexes would have to do something likedf['hashes'].values[df.index.isin(idx)] =
or usedf.index.get_indexer
) - printing/string reps broken, e.g.
print(df)
andprint(df['hashes'])
- trying to access
df[['hashes', 'other_stuff']]
TypeError: void() takes at least 1 positional argument (0 given)
Expected Behavior
can set values with .loc
without assertion error
can print dataframe without exception
Installed Versions
Comment From: digitalsignalperson
also worth mentioning the problems go away if you allow the structured array to have an object dtype
e.g.
import numpy as np
import pandas as pd
N = 10
df = pd.DataFrame(data={'other_stuff':np.zeros(N)})
idx = [0, 1]
hash_dtype = np.dtype([(f'h{i}', np.uint64) for i in range(4)])
df.loc[idx, 'hashes'] = np.ones(len(idx), dtype=hash_dtype)
# here df['hashes'] coerced to object data type
Comment From: rhshadrach
Thanks for the report. Indeed, pandas does not support NumPy structured arrays. I do not think it is feasible to support these.
Comment From: jorisvandenbossche
Should we rather error when a user creates such a Series with an unsupported dtype, instead of allowing to create it but then fail later on in various confusing ways?
Or is there enough that works that people would want to use a Series/DataFrame container with such data?
Comment From: digitalsignalperson
For sure it would be helpful to fail earlier, it took me a while to figure out what led to creating this issue.
My workaround was to view the numpy array as bytes. In my example the structured type is 32 bytes and .astype('S32')
makes each element appear as a 32 bytes string. Pandas seems fine with this data type.
Is there any trick where pandas could do a similar thing, just treat structured arrays as opaque "element is N bytes" or even internally storing as a np.dtype(f'S{element width')
type?
Comment From: rhshadrach
I'm +1 on failing in the constructor.
Is there any trick where pandas could do a similar thing, just treat structured arrays as opaque "element is N bytes" or even internally storing as a
np.dtype(f'S{element width')
type?
I don't think this should happen silently.
Comment From: digitalsignalperson
I don't think this should happen silently.
I was thinking more in terms of something pandas could do internally, but I don't know the internals for why it trips on structured types or if this is a practical idea.
For example maybe some bookkeeping happens so that internally pandas sees a ndarray.view of dtype('S{item size}') but to the user it appears like their custom data type.
N = 10
hash_dtype = np.dtype([(f'h{i}', np.uint64) for i in range(4)])
hashes = np.zeros(N, dtype=hash_dtype)
hashes_view = hashes.view(f'S{hash_dtype.itemsize}')
hashes[0] = (1, 1, 1, 1)
In [35]: hashes
Out[35]:
array([(1, 1, 1, 1), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0),
(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0),
(0, 0, 0, 0), (0, 0, 0, 0)],
dtype=[('h0', '<u8'), ('h1', '<u8'), ('h2', '<u8'), ('h3', '<u8')])
In [37]: print(hashes_view)
[b'\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01'
b'' b'' b'' b'' b'' b'' b'' b'' b'']
Comment From: rhshadrach
For example maybe some bookkeeping happens so that internally pandas sees a ndarray.view of dtype('S{item size}') but to the user it appears like their custom data type.
If this were possible, would essentially any operation other than getting, setting, and reshaping just raise? E.g. groupby.sum. I'm quite negative on partial support for a dtype like this.
Comment From: rhshadrach
Similar to #55011 I think
Comment From: jorisvandenbossche
Another similar bug report in https://github.com/pandas-dev/pandas/issues/42739, with a masked structured array, where for this case the DataFrame constructor already fails, although it could use a more informative error message.
import numpy as np
import pandas as pd
import numpy.ma as ma
# create a masked, structured array
a = np.ma.array([(1, 2.2), (42, 5.5)],
dtype=[('a',int),('b',float)],
mask=[(True,False),(False,True)])
b = pd.DataFrame(a)
Currently gives TypeError: Iterator operand 1 dtype could not be cast from dtype([('a', '?'), ('b', '?')]) to dtype('bool') according to the rule 'unsafe'
Comment From: rhshadrach
We should also fail on a NumPy bytes array in the constructor, e.g.
pd.Series(np.array([b""]))