Open digitalsignalperson opened 4 weeks ago
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
Thanks for the report. Indeed, pandas does not support NumPy structured arrays. I do not think it is feasible to support these.
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?
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?
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.
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'']
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.
Similar to #55011 I think
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'
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
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.
df.loc[idx, 'hashes'] =
get LossySetitemError, AssertionError: Something has gone wrongdf['hashes'].values[idx] =
(but for other indexes would have to do something likedf['hashes'].values[df.index.isin(idx)] =
or usedf.index.get_indexer
)print(df)
andprint(df['hashes'])
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 exceptionInstalled Versions