Skip to content

Commit 546816b

Browse files
committed
ENH: Provide dict object for to_dict() pandas-dev#16122
1 parent 67c57e8 commit 546816b

File tree

5 files changed

+54
-47
lines changed

5 files changed

+54
-47
lines changed

pandas/core/common.py

+12-16
Original file line numberDiff line numberDiff line change
@@ -481,43 +481,39 @@ def _dict_compat(d):
481481
for key, value in iteritems(d))
482482

483483

484-
def prep_maping_for_to_dict(into):
484+
def standardize_mapping(into):
485485
"""
486-
Helper function to standardize the supplied mapping so it can
487-
be passed to the ``Series.to_dict()`` and ``DataFrame.to_dict()``
488-
486+
Helper function to standardize a supplied mapping.
489487
.. versionadded:: 0.21.0
490488
491489
Parameters
492490
----------
493491
into : instance or subclass of collections.Mapping
494-
The argument supplied to ``to_dict``. Must be a class, an
495-
initialized collections.defaultdict, or an empty instance
496-
of a collections.Mapping subclass.
492+
Must be a class, an initialized collections.defaultdict,
493+
or an empty instance of a collections.Mapping subclass.
497494
498495
Returns
499496
-------
500497
mapping : a collections.Mapping subclass or other constructor
501498
a callable object that can accept an iterator to create
502499
the desired Mapping.
503500
501+
See Also
502+
--------
503+
DataFrame.to_dict
504+
Series.to_dict
504505
"""
505506
if not inspect.isclass(into):
506-
if len(into) > 0:
507-
raise ValueError(
508-
"to_dict() only accepts empty mappings.")
509-
elif isinstance(into, collections.defaultdict):
507+
if isinstance(into, collections.defaultdict):
510508
return partial(
511509
collections.defaultdict, into.default_factory)
512-
else:
513-
return prep_maping_for_to_dict(type(into))
514-
elif not issubclass(into, collections.Mapping):
510+
into = type(into)
511+
if not issubclass(into, collections.Mapping):
515512
raise TypeError('unsupported type: {}'.format(into))
516513
elif into == collections.defaultdict:
517514
raise TypeError(
518515
'to_dict() only accepts initialized defaultdicts')
519-
else:
520-
return into
516+
return into
521517

522518

523519
def sentinel_factory():

pandas/core/frame.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
_values_from_object,
6565
_maybe_box_datetimelike,
6666
_dict_compat,
67-
prep_maping_for_to_dict)
67+
standardize_mapping)
6868
from pandas.core.generic import NDFrame, _shared_docs
6969
from pandas.core.index import Index, MultiIndex, _ensure_index
7070
from pandas.core.indexing import (maybe_droplevels, convert_to_index_sliceable,
@@ -889,29 +889,30 @@ def to_dict(self, orient='dict', into=dict):
889889
instance of the mapping type you want. If you want a
890890
collections.defaultdict, you must pass an initialized
891891
instance.
892-
892+
893893
.. versionadded:: 0.21.0
894894
895895
Returns
896896
-------
897897
result : collections.Mapping like {column -> {index -> value}}
898898
If ``into`` is collections.defaultdict, the return
899899
value's default_factory will be None.
900-
900+
901901
Examples
902902
--------
903903
>>> from pandas import DataFrame
904904
>>> from collections import OrderedDict, defaultdict
905-
>>> df = DataFrame({'col1': [1, 2], 'col2': [0.5, 0.75]}, index=['a', 'b'])
905+
>>> df = DataFrame(
906+
{'col1': [1, 2], 'col2': [0.5, 0.75]}, index=['a', 'b'])
906907
>>> df
907908
col1 col2
908909
a 1 0.1
909910
b 2 0.2
910911
>>> df.to_dict()
911912
{'col1': {'a': 1, 'b': 2}, 'col2': {'a': 0.5, 'b': 0.75}}
912-
913+
913914
You can specify the return orientation.
914-
915+
915916
>>> df.to_dict('series')
916917
{'col1': a 1
917918
b 2
@@ -926,22 +927,25 @@ def to_dict(self, orient='dict', into=dict):
926927
[{'col1': 1.0, 'col2': 0.5}, {'col1': 2.0, 'col2': 0.75}]
927928
>>> df.to_dict('index')
928929
{'a': {'col1': 1.0, 'col2': 0.5}, 'b': {'col1': 2.0, 'col2': 0.75}}
929-
930-
You can also specify the mapping type.
931-
930+
931+
You can also specify the mapping type.
932+
932933
>>> df.to_dict(into=OrderedDict)
933-
OrderedDict([('col2', OrderedDict([('a', 0.5), ('b', 0.75)])),
934-
('col1', OrderedDict([('a', 1), ('b', 2)]))])
934+
OrderedDict([('col1', OrderedDict([('a', 1), ('b', 2)])),
935+
('col2', OrderedDict([('a', 0.5), ('b', 0.75)]))])
936+
937+
If you want a `defaultdict`, you need to initialize it:
938+
935939
>>> dd = defaultdict(list)
936940
>>> df.to_dict('records', into=dd)
937-
[defaultdict(list, {'col1': 1.0, 'col2': 0.5}),
938-
defaultdict(list, {'col1': 2.0, 'col2': 0.75})]
941+
[defaultdict(<type 'list'>, {'col2': 0.5, 'col1': 1.0}),
942+
defaultdict(<type 'list'>, {'col2': 0.75, 'col1': 2.0})]
939943
"""
940944
if not self.columns.is_unique:
941945
warnings.warn("DataFrame columns are not unique, some "
942946
"columns will be omitted.", UserWarning)
943947
# GH16122
944-
into_c = prep_maping_for_to_dict(into)
948+
into_c = standardize_mapping(into)
945949
if orient.lower().startswith('d'):
946950
return into_c(
947951
(k, v.to_dict(into)) for k, v in compat.iteritems(self))

pandas/core/series.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
SettingWithCopyError,
4848
_maybe_box_datetimelike,
4949
_dict_compat,
50-
prep_maping_for_to_dict)
50+
standardize_mapping)
5151
from pandas.core.index import (Index, MultiIndex, InvalidIndexError,
5252
Float64Index, _ensure_index)
5353
from pandas.core.indexing import check_bool_indexer, maybe_convert_indices
@@ -1085,15 +1085,15 @@ def to_dict(self, into=dict):
10851085
object. Can be the actual class or an empty
10861086
instance of the mapping type you want. If you want a
10871087
collections.defaultdict, you must pass an initialized
1088-
1088+
10891089
.. versionadded:: 0.21.0
10901090
10911091
Returns
10921092
-------
10931093
value_dict : collections.Mapping
10941094
If ``into`` is collections.defaultdict, the return
10951095
value's default_factory will be None.
1096-
1096+
10971097
Examples
10981098
--------
10991099
>>> from pandas import Series
@@ -1105,11 +1105,10 @@ def to_dict(self, into=dict):
11051105
OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
11061106
>>> dd = defaultdict(list)
11071107
>>> s.to_dict(dd)
1108-
defaultdict(list, {0: 1, 1: 2, 2: 3, 3: 4})
1109-
1108+
defaultdict(<type 'list'>, {0: 1, 1: 2, 2: 3, 3: 4})
11101109
"""
11111110
# GH16122
1112-
into_c = prep_maping_for_to_dict(into)
1111+
into_c = standardize_mapping(into)
11131112
return into_c(compat.iteritems(self))
11141113

11151114
def to_frame(self, name=None):

pandas/tests/frame/test_convert_to.py

+10
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ def test_to_dict(self, mapping):
156156
'A': {'1': 1, '2': 2},
157157
'B': {'1': '1', '2': '2', '3': '3'},
158158
}
159+
159160
# GH16122
160161
recons_data = DataFrame(test_data).to_dict(into=mapping)
161162

@@ -196,6 +197,15 @@ def test_to_dict(self, mapping):
196197
for k2, v2 in compat.iteritems(v):
197198
assert (v2 == recons_data[k2][k])
198199

200+
df = DataFrame(test_data)
201+
df['duped'] = df[df.columns[0]]
202+
recons_data = df.to_dict("i")
203+
comp_data = test_data.copy()
204+
comp_data['duped'] = comp_data[df.columns[0]]
205+
for k, v in compat.iteritems(comp_data):
206+
for k2, v2 in compat.iteritems(v):
207+
assert (v2 == recons_data[k2][k])
208+
199209
@pytest.mark.parametrize('tz', ['UTC', 'GMT', 'US/Eastern'])
200210
def test_to_records_datetimeindex_with_tz(self, tz):
201211
# GH13937

pandas/tests/test_common.py

+9-11
Original file line numberDiff line numberDiff line change
@@ -199,26 +199,24 @@ def test_dict_compat():
199199
assert (com._dict_compat(data_unchanged) == data_unchanged)
200200

201201

202-
def test_prep_maping_for_to_dict():
203-
# No non-empty
204-
bad = {'bad': 'data'}
205-
with pytest.raises(ValueError):
206-
com.prep_maping_for_to_dict(bad)
207-
202+
def test_standardize_mapping():
208203
# No uninitialized defaultdicts
209204
with pytest.raises(TypeError):
210-
com.prep_maping_for_to_dict(collections.defaultdict)
205+
com.standardize_mapping(collections.defaultdict)
211206

212207
# No non-mapping subtypes, instance
213208
with pytest.raises(TypeError):
214-
com.prep_maping_for_to_dict([])
209+
com.standardize_mapping([])
215210

216211
# No non-mapping subtypes, class
217212
with pytest.raises(TypeError):
218-
com.prep_maping_for_to_dict(list)
213+
com.standardize_mapping(list)
214+
215+
fill = {'bad': 'data'}
216+
assert (com.standardize_mapping(fill) == dict)
219217

220218
# Convert instance to type
221-
assert (com.prep_maping_for_to_dict({}) == dict)
219+
assert (com.standardize_mapping({}) == dict)
222220

223221
dd = collections.defaultdict(list)
224-
assert (type(com.prep_maping_for_to_dict(dd)) == partial)
222+
assert isinstance(com.standardize_mapping(dd), partial)

0 commit comments

Comments
 (0)