Skip to content

Commit 4cacb52

Browse files
committed
MAINT: Use is_dict_like in to_records
More generic than checking whether our mappings are instances of dict. Expands is_dict_like check to include whether it has a __contains__ method.
1 parent fa5e1ea commit 4cacb52

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

pandas/core/dtypes/inference.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,11 @@ def is_dict_like(obj):
398398
>>> is_dict_like([1, 2, 3])
399399
False
400400
"""
401+
for attr in ("__getitem__", "keys", "__contains__"):
402+
if not hasattr(obj, attr):
403+
return False
401404

402-
return hasattr(obj, '__getitem__') and hasattr(obj, 'keys')
405+
return True
403406

404407

405408
def is_named_tuple(obj):

pandas/core/frame.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
maybe_upcast_putmask,
4949
find_common_type)
5050
from pandas.core.dtypes.common import (
51+
is_dict_like,
5152
is_object_dtype,
5253
is_extension_type,
5354
is_extension_array_dtype,
@@ -1681,7 +1682,7 @@ def to_records(self, index=True, convert_datetime64=None,
16811682
dtype_mapping = column_dtypes
16821683
name = self.columns[index]
16831684

1684-
if isinstance(dtype_mapping, dict):
1685+
if is_dict_like(dtype_mapping):
16851686
if name in dtype_mapping:
16861687
dtype_mapping = dtype_mapping[name]
16871688
elif index in dtype_mapping:

pandas/tests/frame/test_convert_to.py

+28
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,34 @@ def test_to_records_dtype_mi(self, df, kwargs, expected):
314314
result = df.to_records(**kwargs)
315315
tm.assert_almost_equal(result, expected)
316316

317+
def test_to_records_dict_like(self):
318+
# see gh-18146
319+
class DictLike(object):
320+
def __init__(self, **kwargs):
321+
self.d = kwargs.copy()
322+
323+
def __getitem__(self, key):
324+
return self.d.__getitem__(key)
325+
326+
def __contains__(self, key):
327+
return key in self.d
328+
329+
def keys(self):
330+
return self.d.keys()
331+
332+
df = DataFrame({"A": [1, 2], "B": [0.2, 1.5], "C": ["a", "bc"]})
333+
334+
dtype_mappings = dict(column_dtypes=DictLike(**{"A": np.int8,
335+
"B": np.float32}),
336+
index_dtypes="<U2")
337+
338+
result = df.to_records(**dtype_mappings)
339+
expected = np.rec.array([("0", "1", "0.2", "a"),
340+
("1", "2", "1.5", "bc")],
341+
dtype=[("index", "<U2"), ("A", "i1"),
342+
("B", "<f4"), ("C", "O")])
343+
tm.assert_almost_equal(result, expected)
344+
317345
@pytest.mark.parametrize('mapping', [
318346
dict,
319347
collections.defaultdict(list),

0 commit comments

Comments
 (0)