Skip to content

Commit 5575e93

Browse files
committed
changed according to comments
1 parent 5a89a51 commit 5575e93

File tree

8 files changed

+173
-11
lines changed

8 files changed

+173
-11
lines changed

pandas/_libs/algos.pyx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ from libc.math cimport fabs, sqrt
1010
import numpy as np
1111
cimport numpy as cnp
1212
from numpy cimport (ndarray,
13-
NPY_INT64, NPY_UINT64, NPY_INT32, NPY_INT16, NPY_INT8,
14-
NPY_FLOAT32, NPY_FLOAT64,
13+
NPY_INT64, NPY_INT32, NPY_INT16, NPY_INT8,
14+
NPY_UINT64, NPY_UINT32, NPY_UINT16, NPY_UINT8,
15+
NPY_FLOAT64, NPY_FLOAT32,
1516
NPY_OBJECT,
16-
int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t,
17-
uint32_t, uint64_t, float32_t, float64_t,
17+
int64_t, int32_t, int16_t, int8_t,
18+
uint64_t, uint32_t, uint16_t, uint8_t,
19+
float64_t, float32_t,
1820
double_t)
1921
cnp.import_array()
2022

pandas/_libs/algos_common_helper.pxi.in

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,14 @@ def ensure_object(object arr):
133133
# name, c_type, dtype
134134
dtypes = [('float64', 'FLOAT64', 'float64'),
135135
('float32', 'FLOAT32', 'float32'),
136-
('int8', 'INT8', 'int8'),
137-
('int16', 'INT16', 'int16'),
138-
('int32', 'INT32', 'int32'),
139136
('int64', 'INT64', 'int64'),
137+
('int32', 'INT32', 'int32'),
138+
('int16', 'INT16', 'int16'),
139+
('int8', 'INT8', 'int8'),
140140
('uint64', 'UINT64', 'uint64'),
141+
('uint32', 'UINT32', 'uint32'),
142+
('uint16', 'UINT16', 'uint16'),
143+
('uint8', 'UINT8', 'uint8'),
141144
# ('platform_int', 'INT', 'int_'),
142145
# ('object', 'OBJECT', 'object_'),
143146
]

pandas/_libs/index.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import numpy as np
77
cimport numpy as cnp
88
from numpy cimport (ndarray,
99
float64_t, float32_t,
10-
int64_t,int32_t, int16_t, int8_t,
10+
int64_t, int32_t, int16_t, int8_t,
1111
uint64_t, uint32_t, uint16_t, uint8_t,
1212
intp_t,
1313
# Note: NPY_DATETIME, NPY_TIMEDELTA are only available

pandas/_libs/index_class_helper.pxi.in

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ dtypes = [('Float64', 'float64', 'float64_t'),
3030

3131
cdef class {{name}}Engine(IndexEngine):
3232

33+
_dtype = '{{dtype}}'
34+
3335
def _call_monotonic(self, values):
3436
return algos.is_monotonic_{{dtype}}(values, timelike=False)
3537

@@ -61,6 +63,14 @@ cdef class {{name}}Engine(IndexEngine):
6163
cpdef _call_map_locations(self, values):
6264
# self.mapping is of type Int64HashTable, so convert dtype of values
6365
self.mapping.map_locations(algos.ensure_int64(values))
66+
{{elif name in {'UInt8', 'UInt16', 'UInt32'} }}
67+
cpdef _call_map_locations(self, values):
68+
# self.mapping is of type UInt64HashTable, so convert dtype of values
69+
self.mapping.map_locations(algos.ensure_uint64(values))
70+
{{elif name in {'Float32'} }}
71+
cpdef _call_map_locations(self, values):
72+
# self.mapping is of type Float64HashTable, so convert dtype of values
73+
self.mapping.map_locations(algos.ensure_float64(values))
6474
{{endif}}
6575

6676
{{if name != 'Float64' and name != 'Object'}}
@@ -83,7 +93,7 @@ cdef class {{name}}Engine(IndexEngine):
8393
ndarray[{{ctype}}] values
8494
int count = 0
8595

86-
{{if name != 'Float64'}}
96+
{{if name not in {'Float64', 'Float32'} }}
8797
if not util.is_integer_object(val):
8898
raise KeyError(val)
8999
{{endif}}

pandas/conftest.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,29 @@ def ip():
102102

103103

104104
@pytest.fixture(params=[True, False, None])
105-
def observed(request):
105+
def _true_false_none(request):
106+
"""
107+
Base fixture for fixtures that return True, False and None.
108+
"""
109+
return request.param
110+
111+
112+
@pytest.fixture
113+
def observed(_true_false_none):
106114
""" pass in the observed keyword to groupby for [True, False]
107115
This indicates whether categoricals should return values for
108116
values which are not in the grouper [False / None], or only values which
109117
appear in the grouper [True]. [None] is supported for future compatiblity
110118
if we decide to change the default (and would need to warn if this
111119
parameter is not passed)"""
112-
return request.param
120+
return _true_false_none
121+
122+
123+
@pytest.fixture
124+
def ordered(_true_false_none):
125+
"""Return the allowed parameters for Categorical/CategoricalIndex.ordered.
126+
"""
127+
return _true_false_none
113128

114129

115130
_all_arithmetic_operators = ['__add__', '__radd__',

pandas/tests/indexes/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pandas.util.testing as tm
66
from pandas.core.indexes.api import Index, MultiIndex
7+
from pandas._libs import index as li
78
from pandas.compat import lzip, long
89

910

@@ -45,3 +46,15 @@ def zero(request):
4546
# For testing division by (or of) zero for Index with length 5, this
4647
# gives several scalar-zeros and length-5 vector-zeros
4748
return request.param
49+
50+
51+
@pytest.fixture(
52+
params=[
53+
'Int64', 'Int32', 'Int16', 'Int8',
54+
'UInt64', 'UInt32', 'UInt16', 'UInt8',
55+
'Float64', 'Float32',
56+
])
57+
def num_engine(request):
58+
"""Return the various numeric engines in pd._libs.index
59+
"""
60+
return getattr(li, "{}Engine".format(request.param))

pandas/tests/indexes/test_category.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pandas import Categorical, IntervalIndex, compat
1515
from pandas.util.testing import assert_almost_equal
1616
import pandas.core.config as cf
17+
from pandas._libs import index as li
1718
import pandas as pd
1819

1920
if PY3:
@@ -1117,3 +1118,27 @@ def test_take_invalid_kwargs(self):
11171118
msg = "the 'mode' parameter is not supported"
11181119
tm.assert_raises_regex(ValueError, msg, idx.take,
11191120
indices, mode='clip')
1121+
1122+
1123+
class TestCategoricalIndexEngine(object):
1124+
1125+
@pytest.mark.parametrize('nbits', [8, 16, 32, 64])
1126+
def test_engine_type(self, nbits):
1127+
"""Check that a CategoricalIndex has the correct engine type.
1128+
"""
1129+
if nbits < 64:
1130+
ncategories = int(2 ** (nbits / 2) / 2) # 128 for nbits==16 etc
1131+
index = CategoricalIndex(range(ncategories))
1132+
else:
1133+
index = CategoricalIndex(['a', 'b', 'c'])
1134+
# having actual 2 ** (64 / 2) / 2 categories is too
1135+
# memory-intensive, so we set codes.dtype manually
1136+
index._values._codes = index._values._codes.astype('int64')
1137+
1138+
dtype = {8: np.int8, 16: np.int16,
1139+
32: np.int32, 64: np.int64}[nbits]
1140+
engine = {8: li.Int8Engine, 16: li.Int16Engine,
1141+
32: li.Int32Engine, 64: li.Int64Engine}[nbits]
1142+
1143+
assert isinstance(index._engine, engine)
1144+
assert issubclass(index.codes.dtype.type, dtype)

pandas/tests/indexes/test_engine.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import numpy as np
4+
import pytest
5+
6+
import pandas as pd
7+
from pandas._libs.index import (Int64Engine, UInt64Engine,
8+
Float64Engine, ObjectEngine)
9+
10+
11+
class TestNumericEngine(object):
12+
13+
@pytest.mark.parametrize('data', [[0, 1, 2]])
14+
def test_engine_type(self, data, num_engine):
15+
index = pd.Index(data, dtype=num_engine._dtype)
16+
if issubclass(index.dtype.type, np.signedinteger):
17+
assert isinstance(index._engine, Int64Engine)
18+
elif issubclass(index.dtype.type, np.unsignedinteger):
19+
assert isinstance(index._engine, UInt64Engine)
20+
elif issubclass(index.dtype.type, np.floating):
21+
assert isinstance(index._engine, Float64Engine)
22+
else:
23+
raise TypeError("unexpected dtype {}".format(index.dtype))
24+
25+
@pytest.mark.parametrize('data', [[0, 1, 2]])
26+
def test_is_monotonic_ordered(self, data, num_engine):
27+
codes = np.array(data, dtype=num_engine._dtype)
28+
e = num_engine(lambda: codes, len(codes))
29+
assert e.is_monotonic_increasing
30+
assert not e.is_monotonic_decreasing
31+
32+
# reverse sort order
33+
codes = np.array(list(reversed(data)), dtype=num_engine._dtype)
34+
e = num_engine(lambda: codes, len(codes))
35+
assert not e.is_monotonic_increasing
36+
assert e.is_monotonic_decreasing
37+
38+
@pytest.mark.parametrize('data', [[1, 0, 2]])
39+
def test_is_not_monotonic_ordered(self, data, num_engine):
40+
codes = np.array(data, dtype=num_engine._dtype)
41+
e = num_engine(lambda: codes, len(codes))
42+
assert not e.is_monotonic_increasing
43+
assert not e.is_monotonic_decreasing
44+
45+
@pytest.mark.parametrize('values, expected', [
46+
([1, 2, 3], True),
47+
([1, 1, 2], False),
48+
])
49+
def test_is_unique(self, values, expected, num_engine):
50+
51+
codes = np.array(values, dtype=num_engine._dtype)
52+
e = num_engine(lambda: codes, len(codes))
53+
assert e.is_unique is expected
54+
55+
56+
class TestObjectEngine(object):
57+
58+
def setup_class(cls):
59+
cls.Engine = ObjectEngine
60+
cls.dtype = object
61+
62+
@pytest.mark.parametrize('data', [['a', 'b', 'c']])
63+
def test_engine_type(self, data):
64+
index = pd.Index(data)
65+
assert isinstance(index._engine, self.Engine)
66+
67+
@pytest.mark.parametrize('data', [['a', 'b', 'c']])
68+
def test_is_monotonic_ordered(self, data):
69+
codes = np.array(data, dtype=self.dtype)
70+
e = self.Engine(lambda: codes, len(codes))
71+
assert e.is_monotonic_increasing
72+
assert not e.is_monotonic_decreasing
73+
74+
# reverse sort order
75+
codes = np.array(list(reversed(data)), dtype=self.dtype)
76+
e = self.Engine(lambda: codes, len(codes))
77+
assert not e.is_monotonic_increasing
78+
assert e.is_monotonic_decreasing
79+
80+
@pytest.mark.parametrize('data', [['a', 'c', 'b']])
81+
def test_is_not_monotonic_ordered(self, data):
82+
codes = np.array(data, dtype=self.dtype)
83+
e = self.Engine(lambda: codes, len(codes))
84+
assert not e.is_monotonic_increasing
85+
assert not e.is_monotonic_decreasing
86+
87+
@pytest.mark.parametrize('values, expected', [
88+
(['a', 'b', 'c'], True),
89+
(['a', 'a', 'b'], False),
90+
])
91+
def test_is_unique(self, values, expected):
92+
codes = np.array(values, dtype=self.dtype)
93+
e = self.Engine(lambda: codes, len(codes))
94+
assert e.is_unique is expected

0 commit comments

Comments
 (0)