Skip to content

Commit e2b4574

Browse files
committed
Addresses GH pandas-dev#21918
1 parent b2305ea commit e2b4574

File tree

8 files changed

+663
-235
lines changed

8 files changed

+663
-235
lines changed

pandas/tests/indexes/multi/test_analytics.py

+435
Large diffs are not rendered by default.
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import numpy as np
4+
import pandas as pd
5+
import pandas.util.testing as tm
6+
import pytest
7+
from pandas import (DatetimeIndex, Float64Index, Index, Int64Index, MultiIndex,
8+
PeriodIndex, TimedeltaIndex, UInt64Index, date_range,
9+
period_range)
10+
from pandas.compat import lrange, range
11+
from pandas.core.dtypes.dtypes import CategoricalDtype
12+
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
13+
from pandas.util.testing import assert_copy
14+
15+
16+
def test_astype(idx):
17+
expected = idx.copy()
18+
actual = idx.astype('O')
19+
assert_copy(actual.levels, expected.levels)
20+
assert_copy(actual.labels, expected.labels)
21+
check_level_names(actual, expected.names)
22+
23+
with tm.assert_raises_regex(TypeError, "^Setting.*dtype.*object"):
24+
idx.astype(np.dtype(int))

pandas/tests/indexes/multi/test_constructor.py

+76-53
Original file line numberDiff line numberDiff line change
@@ -234,29 +234,32 @@ def test_from_arrays_empty():
234234
tm.assert_index_equal(result, expected)
235235

236236

237-
def test_from_arrays_invalid_input():
237+
@pytest.mark.parametrize('invalid_array',[
238+
pytest.param(1),
239+
pytest.param([1]),
240+
pytest.param([1, 2]),
241+
pytest.param([[1], 2]),
242+
pytest.param('a'),
243+
pytest.param(['a']),
244+
pytest.param(['a', 'b']),
245+
pytest.param([['a'], 'b']),
246+
]
247+
)
248+
def test_from_arrays_invalid_input(invalid_array):
238249
invalid_inputs = [1, [1], [1, 2], [[1], 2],
239250
'a', ['a'], ['a', 'b'], [['a'], 'b']]
240251
for i in invalid_inputs:
241252
pytest.raises(TypeError, MultiIndex.from_arrays, arrays=i)
242253

243254

244-
def test_from_arrays_different_lengths():
255+
@pytest.mark.parametrize('idx1, idx2',[
256+
pytest.param([1, 2, 3], ['a', 'b']),
257+
pytest.param([], ['a', 'b']),
258+
pytest.param([1, 2, 3], [])
259+
]
260+
)
261+
def test_from_arrays_different_lengths(idx1, idx2):
245262
# see gh-13599
246-
idx1 = [1, 2, 3]
247-
idx2 = ['a', 'b']
248-
tm.assert_raises_regex(ValueError, '^all arrays must '
249-
'be same length$',
250-
MultiIndex.from_arrays, [idx1, idx2])
251-
252-
idx1 = []
253-
idx2 = ['a', 'b']
254-
tm.assert_raises_regex(ValueError, '^all arrays must '
255-
'be same length$',
256-
MultiIndex.from_arrays, [idx1, idx2])
257-
258-
idx1 = [1, 2, 3]
259-
idx2 = []
260263
tm.assert_raises_regex(ValueError, '^all arrays must '
261264
'be same length$',
262265
MultiIndex.from_arrays, [idx1, idx2])
@@ -305,35 +308,41 @@ def test_from_tuples_index_values(idx):
305308
assert (result.values == idx.values).all()
306309

307310

308-
def test_from_product_empty():
311+
def test_from_product_empty_zero_levels():
309312
# 0 levels
310313
with tm.assert_raises_regex(
311314
ValueError, "Must pass non-zero number of levels/labels"):
312315
MultiIndex.from_product([])
313316

314-
# 1 level
317+
318+
def test_from_product_empty_one_level():
315319
result = MultiIndex.from_product([[]], names=['A'])
316320
expected = pd.Index([], name='A')
317321
tm.assert_index_equal(result.levels[0], expected)
318322

319-
# 2 levels
320-
l1 = [[], ['foo', 'bar', 'baz'], []]
321-
l2 = [[], [], ['a', 'b', 'c']]
323+
324+
@pytest.mark.parametrize('first, second',[
325+
([],[]),
326+
(['foo', 'bar', 'baz'], []),
327+
([], ['a', 'b', 'c']),
328+
])
329+
def test_from_product_empty_two_levels(first, second):
322330
names = ['A', 'B']
323-
for first, second in zip(l1, l2):
324-
result = MultiIndex.from_product([first, second], names=names)
325-
expected = MultiIndex(levels=[first, second],
326-
labels=[[], []], names=names)
327-
tm.assert_index_equal(result, expected)
331+
result = MultiIndex.from_product([first, second], names=names)
332+
expected = MultiIndex(levels=[first, second],
333+
labels=[[], []], names=names)
334+
tm.assert_index_equal(result, expected)
335+
328336

337+
@pytest.mark.parametrize('N', list(range(4)))
338+
def test_from_product_empty_three_levels(N):
329339
# GH12258
330340
names = ['A', 'B', 'C']
331-
for N in range(4):
332-
lvl2 = lrange(N)
333-
result = MultiIndex.from_product([[], lvl2, []], names=names)
334-
expected = MultiIndex(levels=[[], lvl2, []],
335-
labels=[[], [], []], names=names)
336-
tm.assert_index_equal(result, expected)
341+
lvl2 = lrange(N)
342+
result = MultiIndex.from_product([[], lvl2, []], names=names)
343+
expected = MultiIndex(levels=[[], lvl2, []],
344+
labels=[[], [], []], names=names)
345+
tm.assert_index_equal(result, expected)
337346

338347

339348
def test_from_product_invalid_input():
@@ -352,19 +361,24 @@ def test_from_product_datetimeindex():
352361
tm.assert_numpy_array_equal(mi.values, etalon)
353362

354363

355-
def test_from_product_index_series_categorical():
364+
@pytest.mark.parametrize('ordered', [False, True])
365+
@pytest.mark.parametrize('f', [
366+
lambda x: x,
367+
lambda x: pd.Series(x),
368+
lambda x: x.values
369+
])
370+
def test_from_product_index_series_categorical(ordered, f):
356371
# GH13743
357372
first = ['foo', 'bar']
358-
for ordered in [False, True]:
359-
idx = pd.CategoricalIndex(list("abcaab"), categories=list("bac"),
360-
ordered=ordered)
361-
expected = pd.CategoricalIndex(list("abcaab") + list("abcaab"),
362-
categories=list("bac"),
363-
ordered=ordered)
373+
374+
idx = pd.CategoricalIndex(list("abcaab"), categories=list("bac"),
375+
ordered=ordered)
376+
expected = pd.CategoricalIndex(list("abcaab") + list("abcaab"),
377+
categories=list("bac"),
378+
ordered=ordered)
364379

365-
for arr in [idx, pd.Series(idx), idx.values]:
366-
result = pd.MultiIndex.from_product([first, arr])
367-
tm.assert_index_equal(result.get_level_values(1), expected)
380+
result = pd.MultiIndex.from_product([first, f(idx)])
381+
tm.assert_index_equal(result.get_level_values(1), expected)
368382

369383

370384
def test_from_product():
@@ -409,19 +423,28 @@ def test_create_index_existing_name(idx):
409423
index = idx
410424
index.names = ['foo', 'bar']
411425
result = pd.Index(index)
412-
tm.assert_index_equal(
413-
result, Index(Index([('foo', 'one'), ('foo', 'two'),
414-
('bar', 'one'), ('baz', 'two'),
415-
('qux', 'one'), ('qux', 'two')],
416-
dtype='object'),
417-
names=['foo', 'bar']))
426+
expected = Index(
427+
Index([
428+
('foo', 'one'), ('foo', 'two'),
429+
('bar', 'one'), ('baz', 'two'),
430+
('qux', 'one'), ('qux', 'two')],
431+
dtype='object'
432+
),
433+
names=['foo', 'bar']
434+
)
435+
tm.assert_index_equal(result, expected)
418436

419437
result = pd.Index(index, names=['A', 'B'])
420-
tm.assert_index_equal(
421-
result,
422-
Index(Index([('foo', 'one'), ('foo', 'two'), ('bar', 'one'),
423-
('baz', 'two'), ('qux', 'one'), ('qux', 'two')],
424-
dtype='object'), names=['A', 'B']))
438+
expected = Index(
439+
Index([
440+
('foo', 'one'), ('foo', 'two'),
441+
('bar', 'one'), ('baz', 'two'),
442+
('qux', 'one'), ('qux', 'two')],
443+
dtype='object'
444+
),
445+
names=['A', 'B']
446+
)
447+
tm.assert_index_equal(result, expected)
425448

426449

427450
def test_tuples_with_name_string():

pandas/tests/indexes/multi/test_copy.py

+47-82
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
from copy import copy, deepcopy
44

5+
import numpy as np
56
import pandas.util.testing as tm
7+
import pytest
68
from pandas import (CategoricalIndex, IntervalIndex, MultiIndex, PeriodIndex,
79
RangeIndex, Series, compat)
8-
10+
from pandas.core.indexes.frozen import FrozenList
911

1012
def assert_multiindex_copied(copy, original):
1113
# Levels should be (at least, shallow copied)
@@ -41,84 +43,47 @@ def test_view(idx):
4143
assert_multiindex_copied(i_view, idx)
4244

4345

44-
def test_copy_name(idx):
45-
# gh-12309: Check that the "name" argument
46-
# passed at initialization is honored.
47-
48-
# TODO: Remove or refactor MultiIndex not tested.
49-
for name, index in compat.iteritems({'idx': idx}):
50-
if isinstance(index, MultiIndex):
51-
continue
52-
53-
first = index.__class__(index, copy=True, name='mario')
54-
second = first.__class__(first, copy=False)
55-
56-
# Even though "copy=False", we want a new object.
57-
assert first is not second
58-
59-
# Not using tm.assert_index_equal() since names differ.
60-
assert index.equals(first)
61-
62-
assert first.name == 'mario'
63-
assert second.name == 'mario'
64-
65-
s1 = Series(2, index=first)
66-
s2 = Series(3, index=second[:-1])
67-
68-
if not isinstance(index, CategoricalIndex):
69-
# See gh-13365
70-
s3 = s1 * s2
71-
assert s3.index.name == 'mario'
72-
73-
74-
def test_ensure_copied_data(idx):
75-
# Check the "copy" argument of each Index.__new__ is honoured
76-
# GH12309
77-
# TODO: REMOVE THIS TEST. MultiIndex is tested seperately as noted below.
78-
79-
for name, index in compat.iteritems({'idx': idx}):
80-
init_kwargs = {}
81-
if isinstance(index, PeriodIndex):
82-
# Needs "freq" specification:
83-
init_kwargs['freq'] = index.freq
84-
elif isinstance(index, (RangeIndex, MultiIndex, CategoricalIndex)):
85-
# RangeIndex cannot be initialized from data
86-
# MultiIndex and CategoricalIndex are tested separately
87-
continue
88-
89-
index_type = index.__class__
90-
result = index_type(index.values, copy=True, **init_kwargs)
91-
tm.assert_index_equal(index, result)
92-
tm.assert_numpy_array_equal(index.values, result.values,
93-
check_same='copy')
94-
95-
if isinstance(index, PeriodIndex):
96-
# .values an object array of Period, thus copied
97-
result = index_type(ordinal=index.asi8, copy=False,
98-
**init_kwargs)
99-
tm.assert_numpy_array_equal(index._ndarray_values,
100-
result._ndarray_values,
101-
check_same='same')
102-
elif isinstance(index, IntervalIndex):
103-
# checked in test_interval.py
104-
pass
105-
else:
106-
result = index_type(index.values, copy=False, **init_kwargs)
107-
tm.assert_numpy_array_equal(index.values, result.values,
108-
check_same='same')
109-
tm.assert_numpy_array_equal(index._ndarray_values,
110-
result._ndarray_values,
111-
check_same='same')
112-
113-
114-
def test_copy_and_deepcopy(indices):
115-
116-
if isinstance(indices, MultiIndex):
117-
return
118-
for func in (copy, deepcopy):
119-
idx_copy = func(indices)
120-
assert idx_copy is not indices
121-
assert idx_copy.equals(indices)
122-
123-
new_copy = indices.copy(deep=True, name="banana")
124-
assert new_copy.name == "banana"
46+
@pytest.mark.parametrize('func', [copy, deepcopy])
47+
def test_copy_and_deepcopy(func):
48+
49+
idx = MultiIndex(
50+
levels=[['foo', 'bar'],['fizz', 'buzz']],
51+
labels=[[0, 0, 0, 1],[0, 0, 1, 1]],
52+
names=['first', 'second']
53+
)
54+
idx_copy = func(idx)
55+
assert idx_copy is not idx
56+
assert idx_copy.equals(idx)
57+
58+
59+
@pytest.mark.parametrize('deep', [True, False])
60+
def test_copy_method(deep):
61+
idx = MultiIndex(
62+
levels=[['foo', 'bar'],['fizz', 'buzz']],
63+
labels=[[0, 0, 0, 1],[0, 0, 1, 1]],
64+
names=['first', 'second']
65+
)
66+
idx_copy = idx.copy(deep=deep)
67+
assert idx_copy.equals(idx)
68+
69+
70+
# TODO: dtypes?
71+
@pytest.mark.parametrize('deep', [True, False])
72+
@pytest.mark.parametrize('kwarg, value',[
73+
('names', ['thrid', 'fourth']),
74+
('levels', [['foo2', 'bar2'],['fizz2', 'buzz2']]),
75+
('labels', [[1, 0, 0, 0], [1, 1, 0, 0]])
76+
])
77+
def test_copy_method_kwargs(deep, kwarg, value):
78+
# gh-12309: Check that the "name" argument as well other kwargs
79+
idx = MultiIndex(
80+
levels=[['foo', 'bar'],['fizz', 'buzz']],
81+
labels=[[0, 0, 0, 1],[0, 0, 1, 1]],
82+
names=['first', 'second']
83+
)
84+
85+
idx_copy = idx.copy(**{kwarg: value}, deep=deep)
86+
if kwarg == 'names':
87+
getattr(idx_copy, kwarg) == value
88+
else:
89+
assert list(list(i) for i in getattr(idx_copy, kwarg)) == value

pandas/tests/indexes/multi/test_format.py

-5
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@ def test_repr_roundtrip():
100100
tm.assert_index_equal(result, mi_u, exact=True)
101101

102102

103-
def test_str():
104-
# tested elsewhere
105-
pass
106-
107-
108103
def test_unicode_string_with_unicode():
109104
d = {"a": [u("\u05d0"), 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}
110105
idx = pd.DataFrame(d).set_index(["a", "b"]).index

0 commit comments

Comments
 (0)