894 lines
32 KiB
Python
894 lines
32 KiB
Python
|
from datetime import datetime, timedelta
|
||
|
import inspect
|
||
|
from itertools import permutations
|
||
|
|
||
|
import numpy as np
|
||
|
import pytest
|
||
|
|
||
|
import pandas as pd
|
||
|
from pandas import (
|
||
|
Categorical,
|
||
|
CategoricalIndex,
|
||
|
DataFrame,
|
||
|
Index,
|
||
|
MultiIndex,
|
||
|
Series,
|
||
|
date_range,
|
||
|
isna,
|
||
|
)
|
||
|
import pandas._testing as tm
|
||
|
from pandas.api.types import CategoricalDtype as CDT
|
||
|
import pandas.core.common as com
|
||
|
|
||
|
|
||
|
class TestDataFrameSelectReindex:
|
||
|
# These are specific reindex-based tests; other indexing tests should go in
|
||
|
# test_indexing
|
||
|
|
||
|
def test_reindex_with_multi_index(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/29896
|
||
|
# tests for reindexing a multi-indexed DataFrame with a new MultiIndex
|
||
|
#
|
||
|
# confirms that we can reindex a multi-indexed DataFrame with a new
|
||
|
# MultiIndex object correctly when using no filling, backfilling, and
|
||
|
# padding
|
||
|
#
|
||
|
# The DataFrame, `df`, used in this test is:
|
||
|
# c
|
||
|
# a b
|
||
|
# -1 0 A
|
||
|
# 1 B
|
||
|
# 2 C
|
||
|
# 3 D
|
||
|
# 4 E
|
||
|
# 5 F
|
||
|
# 6 G
|
||
|
# 0 0 A
|
||
|
# 1 B
|
||
|
# 2 C
|
||
|
# 3 D
|
||
|
# 4 E
|
||
|
# 5 F
|
||
|
# 6 G
|
||
|
# 1 0 A
|
||
|
# 1 B
|
||
|
# 2 C
|
||
|
# 3 D
|
||
|
# 4 E
|
||
|
# 5 F
|
||
|
# 6 G
|
||
|
#
|
||
|
# and the other MultiIndex, `new_multi_index`, is:
|
||
|
# 0: 0 0.5
|
||
|
# 1: 2.0
|
||
|
# 2: 5.0
|
||
|
# 3: 5.8
|
||
|
df = DataFrame(
|
||
|
{
|
||
|
"a": [-1] * 7 + [0] * 7 + [1] * 7,
|
||
|
"b": list(range(7)) * 3,
|
||
|
"c": ["A", "B", "C", "D", "E", "F", "G"] * 3,
|
||
|
}
|
||
|
).set_index(["a", "b"])
|
||
|
new_index = [0.5, 2.0, 5.0, 5.8]
|
||
|
new_multi_index = MultiIndex.from_product([[0], new_index], names=["a", "b"])
|
||
|
|
||
|
# reindexing w/o a `method` value
|
||
|
reindexed = df.reindex(new_multi_index)
|
||
|
expected = DataFrame(
|
||
|
{"a": [0] * 4, "b": new_index, "c": [np.nan, "C", "F", np.nan]}
|
||
|
).set_index(["a", "b"])
|
||
|
tm.assert_frame_equal(expected, reindexed)
|
||
|
|
||
|
# reindexing with backfilling
|
||
|
expected = DataFrame(
|
||
|
{"a": [0] * 4, "b": new_index, "c": ["B", "C", "F", "G"]}
|
||
|
).set_index(["a", "b"])
|
||
|
reindexed_with_backfilling = df.reindex(new_multi_index, method="bfill")
|
||
|
tm.assert_frame_equal(expected, reindexed_with_backfilling)
|
||
|
|
||
|
reindexed_with_backfilling = df.reindex(new_multi_index, method="backfill")
|
||
|
tm.assert_frame_equal(expected, reindexed_with_backfilling)
|
||
|
|
||
|
# reindexing with padding
|
||
|
expected = DataFrame(
|
||
|
{"a": [0] * 4, "b": new_index, "c": ["A", "C", "F", "F"]}
|
||
|
).set_index(["a", "b"])
|
||
|
reindexed_with_padding = df.reindex(new_multi_index, method="pad")
|
||
|
tm.assert_frame_equal(expected, reindexed_with_padding)
|
||
|
|
||
|
reindexed_with_padding = df.reindex(new_multi_index, method="ffill")
|
||
|
tm.assert_frame_equal(expected, reindexed_with_padding)
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"method,expected_values",
|
||
|
[
|
||
|
("nearest", [0, 1, 1, 2]),
|
||
|
("pad", [np.nan, 0, 1, 1]),
|
||
|
("backfill", [0, 1, 2, 2]),
|
||
|
],
|
||
|
)
|
||
|
def test_reindex_methods(self, method, expected_values):
|
||
|
df = DataFrame({"x": list(range(5))})
|
||
|
target = np.array([-0.1, 0.9, 1.1, 1.5])
|
||
|
|
||
|
expected = DataFrame({"x": expected_values}, index=target)
|
||
|
actual = df.reindex(target, method=method)
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
actual = df.reindex(target, method=method, tolerance=1)
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
actual = df.reindex(target, method=method, tolerance=[1, 1, 1, 1])
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
e2 = expected[::-1]
|
||
|
actual = df.reindex(target[::-1], method=method)
|
||
|
tm.assert_frame_equal(e2, actual)
|
||
|
|
||
|
new_order = [3, 0, 2, 1]
|
||
|
e2 = expected.iloc[new_order]
|
||
|
actual = df.reindex(target[new_order], method=method)
|
||
|
tm.assert_frame_equal(e2, actual)
|
||
|
|
||
|
switched_method = (
|
||
|
"pad" if method == "backfill" else "backfill" if method == "pad" else method
|
||
|
)
|
||
|
actual = df[::-1].reindex(target, method=switched_method)
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
def test_reindex_methods_nearest_special(self):
|
||
|
df = DataFrame({"x": list(range(5))})
|
||
|
target = np.array([-0.1, 0.9, 1.1, 1.5])
|
||
|
|
||
|
expected = DataFrame({"x": [0, 1, 1, np.nan]}, index=target)
|
||
|
actual = df.reindex(target, method="nearest", tolerance=0.2)
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
expected = DataFrame({"x": [0, np.nan, 1, np.nan]}, index=target)
|
||
|
actual = df.reindex(target, method="nearest", tolerance=[0.5, 0.01, 0.4, 0.1])
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
def test_reindex_nearest_tz(self, tz_aware_fixture):
|
||
|
# GH26683
|
||
|
tz = tz_aware_fixture
|
||
|
idx = pd.date_range("2019-01-01", periods=5, tz=tz)
|
||
|
df = DataFrame({"x": list(range(5))}, index=idx)
|
||
|
|
||
|
expected = df.head(3)
|
||
|
actual = df.reindex(idx[:3], method="nearest")
|
||
|
tm.assert_frame_equal(expected, actual)
|
||
|
|
||
|
def test_reindex_nearest_tz_empty_frame(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/31964
|
||
|
dti = pd.DatetimeIndex(["2016-06-26 14:27:26+00:00"])
|
||
|
df = DataFrame(index=pd.DatetimeIndex(["2016-07-04 14:00:59+00:00"]))
|
||
|
expected = DataFrame(index=dti)
|
||
|
result = df.reindex(dti, method="nearest")
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_frame_add_nat(self):
|
||
|
rng = date_range("1/1/2000 00:00:00", periods=10, freq="10s")
|
||
|
df = DataFrame({"A": np.random.randn(len(rng)), "B": rng})
|
||
|
|
||
|
result = df.reindex(range(15))
|
||
|
assert np.issubdtype(result["B"].dtype, np.dtype("M8[ns]"))
|
||
|
|
||
|
mask = com.isna(result)["B"]
|
||
|
assert mask[-5:].all()
|
||
|
assert not mask[:-5].any()
|
||
|
|
||
|
def test_reindex_limit(self):
|
||
|
# GH 28631
|
||
|
data = [["A", "A", "A"], ["B", "B", "B"], ["C", "C", "C"], ["D", "D", "D"]]
|
||
|
exp_data = [
|
||
|
["A", "A", "A"],
|
||
|
["B", "B", "B"],
|
||
|
["C", "C", "C"],
|
||
|
["D", "D", "D"],
|
||
|
["D", "D", "D"],
|
||
|
[np.nan, np.nan, np.nan],
|
||
|
]
|
||
|
df = DataFrame(data)
|
||
|
result = df.reindex([0, 1, 2, 3, 4, 5], method="ffill", limit=1)
|
||
|
expected = DataFrame(exp_data)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_level(self):
|
||
|
icol = ["jim", "joe", "jolie"]
|
||
|
|
||
|
def verify_first_level(df, level, idx, check_index_type=True):
|
||
|
def f(val):
|
||
|
return np.nonzero((df[level] == val).to_numpy())[0]
|
||
|
|
||
|
i = np.concatenate(list(map(f, idx)))
|
||
|
left = df.set_index(icol).reindex(idx, level=level)
|
||
|
right = df.iloc[i].set_index(icol)
|
||
|
tm.assert_frame_equal(left, right, check_index_type=check_index_type)
|
||
|
|
||
|
def verify(df, level, idx, indexer, check_index_type=True):
|
||
|
left = df.set_index(icol).reindex(idx, level=level)
|
||
|
right = df.iloc[indexer].set_index(icol)
|
||
|
tm.assert_frame_equal(left, right, check_index_type=check_index_type)
|
||
|
|
||
|
df = DataFrame(
|
||
|
{
|
||
|
"jim": list("B" * 4 + "A" * 2 + "C" * 3),
|
||
|
"joe": list("abcdeabcd")[::-1],
|
||
|
"jolie": [10, 20, 30] * 3,
|
||
|
"joline": np.random.randint(0, 1000, 9),
|
||
|
}
|
||
|
)
|
||
|
|
||
|
target = [
|
||
|
["C", "B", "A"],
|
||
|
["F", "C", "A", "D"],
|
||
|
["A"],
|
||
|
["A", "B", "C"],
|
||
|
["C", "A", "B"],
|
||
|
["C", "B"],
|
||
|
["C", "A"],
|
||
|
["A", "B"],
|
||
|
["B", "A", "C"],
|
||
|
]
|
||
|
|
||
|
for idx in target:
|
||
|
verify_first_level(df, "jim", idx)
|
||
|
|
||
|
# reindex by these causes different MultiIndex levels
|
||
|
for idx in [["D", "F"], ["A", "C", "B"]]:
|
||
|
verify_first_level(df, "jim", idx, check_index_type=False)
|
||
|
|
||
|
verify(df, "joe", list("abcde"), [3, 2, 1, 0, 5, 4, 8, 7, 6])
|
||
|
verify(df, "joe", list("abcd"), [3, 2, 1, 0, 5, 8, 7, 6])
|
||
|
verify(df, "joe", list("abc"), [3, 2, 1, 8, 7, 6])
|
||
|
verify(df, "joe", list("eca"), [1, 3, 4, 6, 8])
|
||
|
verify(df, "joe", list("edc"), [0, 1, 4, 5, 6])
|
||
|
verify(df, "joe", list("eadbc"), [3, 0, 2, 1, 4, 5, 8, 7, 6])
|
||
|
verify(df, "joe", list("edwq"), [0, 4, 5])
|
||
|
verify(df, "joe", list("wq"), [], check_index_type=False)
|
||
|
|
||
|
df = DataFrame(
|
||
|
{
|
||
|
"jim": ["mid"] * 5 + ["btm"] * 8 + ["top"] * 7,
|
||
|
"joe": ["3rd"] * 2
|
||
|
+ ["1st"] * 3
|
||
|
+ ["2nd"] * 3
|
||
|
+ ["1st"] * 2
|
||
|
+ ["3rd"] * 3
|
||
|
+ ["1st"] * 2
|
||
|
+ ["3rd"] * 3
|
||
|
+ ["2nd"] * 2,
|
||
|
# this needs to be jointly unique with jim and joe or
|
||
|
# reindexing will fail ~1.5% of the time, this works
|
||
|
# out to needing unique groups of same size as joe
|
||
|
"jolie": np.concatenate(
|
||
|
[
|
||
|
np.random.choice(1000, x, replace=False)
|
||
|
for x in [2, 3, 3, 2, 3, 2, 3, 2]
|
||
|
]
|
||
|
),
|
||
|
"joline": np.random.randn(20).round(3) * 10,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
for idx in permutations(df["jim"].unique()):
|
||
|
for i in range(3):
|
||
|
verify_first_level(df, "jim", idx[: i + 1])
|
||
|
|
||
|
i = [2, 3, 4, 0, 1, 8, 9, 5, 6, 7, 10, 11, 12, 13, 14, 18, 19, 15, 16, 17]
|
||
|
verify(df, "joe", ["1st", "2nd", "3rd"], i)
|
||
|
|
||
|
i = [0, 1, 2, 3, 4, 10, 11, 12, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19, 13, 14]
|
||
|
verify(df, "joe", ["3rd", "2nd", "1st"], i)
|
||
|
|
||
|
i = [0, 1, 5, 6, 7, 10, 11, 12, 18, 19, 15, 16, 17]
|
||
|
verify(df, "joe", ["2nd", "3rd"], i)
|
||
|
|
||
|
i = [0, 1, 2, 3, 4, 10, 11, 12, 8, 9, 15, 16, 17, 13, 14]
|
||
|
verify(df, "joe", ["3rd", "1st"], i)
|
||
|
|
||
|
def test_non_monotonic_reindex_methods(self):
|
||
|
dr = date_range("2013-08-01", periods=6, freq="B")
|
||
|
data = np.random.randn(6, 1)
|
||
|
df = DataFrame(data, index=dr, columns=list("A"))
|
||
|
df_rev = DataFrame(data, index=dr[[3, 4, 5] + [0, 1, 2]], columns=list("A"))
|
||
|
# index is not monotonic increasing or decreasing
|
||
|
msg = "index must be monotonic increasing or decreasing"
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df_rev.reindex(df.index, method="pad")
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df_rev.reindex(df.index, method="ffill")
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df_rev.reindex(df.index, method="bfill")
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df_rev.reindex(df.index, method="nearest")
|
||
|
|
||
|
def test_reindex_sparse(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/35286
|
||
|
df = DataFrame(
|
||
|
{"A": [0, 1], "B": pd.array([0, 1], dtype=pd.SparseDtype("int64", 0))}
|
||
|
)
|
||
|
result = df.reindex([0, 2])
|
||
|
expected = DataFrame(
|
||
|
{
|
||
|
"A": [0.0, np.nan],
|
||
|
"B": pd.array([0.0, np.nan], dtype=pd.SparseDtype("float64", 0.0)),
|
||
|
},
|
||
|
index=[0, 2],
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex(self, float_frame):
|
||
|
datetime_series = tm.makeTimeSeries(nper=30)
|
||
|
|
||
|
newFrame = float_frame.reindex(datetime_series.index)
|
||
|
|
||
|
for col in newFrame.columns:
|
||
|
for idx, val in newFrame[col].items():
|
||
|
if idx in float_frame.index:
|
||
|
if np.isnan(val):
|
||
|
assert np.isnan(float_frame[col][idx])
|
||
|
else:
|
||
|
assert val == float_frame[col][idx]
|
||
|
else:
|
||
|
assert np.isnan(val)
|
||
|
|
||
|
for col, series in newFrame.items():
|
||
|
assert tm.equalContents(series.index, newFrame.index)
|
||
|
emptyFrame = float_frame.reindex(Index([]))
|
||
|
assert len(emptyFrame.index) == 0
|
||
|
|
||
|
# Cython code should be unit-tested directly
|
||
|
nonContigFrame = float_frame.reindex(datetime_series.index[::2])
|
||
|
|
||
|
for col in nonContigFrame.columns:
|
||
|
for idx, val in nonContigFrame[col].items():
|
||
|
if idx in float_frame.index:
|
||
|
if np.isnan(val):
|
||
|
assert np.isnan(float_frame[col][idx])
|
||
|
else:
|
||
|
assert val == float_frame[col][idx]
|
||
|
else:
|
||
|
assert np.isnan(val)
|
||
|
|
||
|
for col, series in nonContigFrame.items():
|
||
|
assert tm.equalContents(series.index, nonContigFrame.index)
|
||
|
|
||
|
# corner cases
|
||
|
|
||
|
# Same index, copies values but not index if copy=False
|
||
|
newFrame = float_frame.reindex(float_frame.index, copy=False)
|
||
|
assert newFrame.index is float_frame.index
|
||
|
|
||
|
# length zero
|
||
|
newFrame = float_frame.reindex([])
|
||
|
assert newFrame.empty
|
||
|
assert len(newFrame.columns) == len(float_frame.columns)
|
||
|
|
||
|
# length zero with columns reindexed with non-empty index
|
||
|
newFrame = float_frame.reindex([])
|
||
|
newFrame = newFrame.reindex(float_frame.index)
|
||
|
assert len(newFrame.index) == len(float_frame.index)
|
||
|
assert len(newFrame.columns) == len(float_frame.columns)
|
||
|
|
||
|
# pass non-Index
|
||
|
newFrame = float_frame.reindex(list(datetime_series.index))
|
||
|
expected = datetime_series.index._with_freq(None)
|
||
|
tm.assert_index_equal(newFrame.index, expected)
|
||
|
|
||
|
# copy with no axes
|
||
|
result = float_frame.reindex()
|
||
|
tm.assert_frame_equal(result, float_frame)
|
||
|
assert result is not float_frame
|
||
|
|
||
|
def test_reindex_nan(self):
|
||
|
df = DataFrame(
|
||
|
[[1, 2], [3, 5], [7, 11], [9, 23]],
|
||
|
index=[2, np.nan, 1, 5],
|
||
|
columns=["joe", "jim"],
|
||
|
)
|
||
|
|
||
|
i, j = [np.nan, 5, 5, np.nan, 1, 2, np.nan], [1, 3, 3, 1, 2, 0, 1]
|
||
|
tm.assert_frame_equal(df.reindex(i), df.iloc[j])
|
||
|
|
||
|
df.index = df.index.astype("object")
|
||
|
tm.assert_frame_equal(df.reindex(i), df.iloc[j], check_index_type=False)
|
||
|
|
||
|
# GH10388
|
||
|
df = DataFrame(
|
||
|
{
|
||
|
"other": ["a", "b", np.nan, "c"],
|
||
|
"date": ["2015-03-22", np.nan, "2012-01-08", np.nan],
|
||
|
"amount": [2, 3, 4, 5],
|
||
|
}
|
||
|
)
|
||
|
|
||
|
df["date"] = pd.to_datetime(df.date)
|
||
|
df["delta"] = (pd.to_datetime("2015-06-18") - df["date"]).shift(1)
|
||
|
|
||
|
left = df.set_index(["delta", "other", "date"]).reset_index()
|
||
|
right = df.reindex(columns=["delta", "other", "date", "amount"])
|
||
|
tm.assert_frame_equal(left, right)
|
||
|
|
||
|
def test_reindex_name_remains(self):
|
||
|
s = Series(np.random.rand(10))
|
||
|
df = DataFrame(s, index=np.arange(len(s)))
|
||
|
i = Series(np.arange(10), name="iname")
|
||
|
|
||
|
df = df.reindex(i)
|
||
|
assert df.index.name == "iname"
|
||
|
|
||
|
df = df.reindex(Index(np.arange(10), name="tmpname"))
|
||
|
assert df.index.name == "tmpname"
|
||
|
|
||
|
s = Series(np.random.rand(10))
|
||
|
df = DataFrame(s.T, index=np.arange(len(s)))
|
||
|
i = Series(np.arange(10), name="iname")
|
||
|
df = df.reindex(columns=i)
|
||
|
assert df.columns.name == "iname"
|
||
|
|
||
|
def test_reindex_int(self, int_frame):
|
||
|
smaller = int_frame.reindex(int_frame.index[::2])
|
||
|
|
||
|
assert smaller["A"].dtype == np.int64
|
||
|
|
||
|
bigger = smaller.reindex(int_frame.index)
|
||
|
assert bigger["A"].dtype == np.float64
|
||
|
|
||
|
smaller = int_frame.reindex(columns=["A", "B"])
|
||
|
assert smaller["A"].dtype == np.int64
|
||
|
|
||
|
def test_reindex_columns(self, float_frame):
|
||
|
new_frame = float_frame.reindex(columns=["A", "B", "E"])
|
||
|
|
||
|
tm.assert_series_equal(new_frame["B"], float_frame["B"])
|
||
|
assert np.isnan(new_frame["E"]).all()
|
||
|
assert "C" not in new_frame
|
||
|
|
||
|
# Length zero
|
||
|
new_frame = float_frame.reindex(columns=[])
|
||
|
assert new_frame.empty
|
||
|
|
||
|
def test_reindex_columns_method(self):
|
||
|
|
||
|
# GH 14992, reindexing over columns ignored method
|
||
|
df = DataFrame(
|
||
|
data=[[11, 12, 13], [21, 22, 23], [31, 32, 33]],
|
||
|
index=[1, 2, 4],
|
||
|
columns=[1, 2, 4],
|
||
|
dtype=float,
|
||
|
)
|
||
|
|
||
|
# default method
|
||
|
result = df.reindex(columns=range(6))
|
||
|
expected = DataFrame(
|
||
|
data=[
|
||
|
[np.nan, 11, 12, np.nan, 13, np.nan],
|
||
|
[np.nan, 21, 22, np.nan, 23, np.nan],
|
||
|
[np.nan, 31, 32, np.nan, 33, np.nan],
|
||
|
],
|
||
|
index=[1, 2, 4],
|
||
|
columns=range(6),
|
||
|
dtype=float,
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
# method='ffill'
|
||
|
result = df.reindex(columns=range(6), method="ffill")
|
||
|
expected = DataFrame(
|
||
|
data=[
|
||
|
[np.nan, 11, 12, 12, 13, 13],
|
||
|
[np.nan, 21, 22, 22, 23, 23],
|
||
|
[np.nan, 31, 32, 32, 33, 33],
|
||
|
],
|
||
|
index=[1, 2, 4],
|
||
|
columns=range(6),
|
||
|
dtype=float,
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
# method='bfill'
|
||
|
result = df.reindex(columns=range(6), method="bfill")
|
||
|
expected = DataFrame(
|
||
|
data=[
|
||
|
[11, 11, 12, 13, 13, np.nan],
|
||
|
[21, 21, 22, 23, 23, np.nan],
|
||
|
[31, 31, 32, 33, 33, np.nan],
|
||
|
],
|
||
|
index=[1, 2, 4],
|
||
|
columns=range(6),
|
||
|
dtype=float,
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_axes(self):
|
||
|
# GH 3317, reindexing by both axes loses freq of the index
|
||
|
df = DataFrame(
|
||
|
np.ones((3, 3)),
|
||
|
index=[datetime(2012, 1, 1), datetime(2012, 1, 2), datetime(2012, 1, 3)],
|
||
|
columns=["a", "b", "c"],
|
||
|
)
|
||
|
time_freq = date_range("2012-01-01", "2012-01-03", freq="d")
|
||
|
some_cols = ["a", "b"]
|
||
|
|
||
|
index_freq = df.reindex(index=time_freq).index.freq
|
||
|
both_freq = df.reindex(index=time_freq, columns=some_cols).index.freq
|
||
|
seq_freq = df.reindex(index=time_freq).reindex(columns=some_cols).index.freq
|
||
|
assert index_freq == both_freq
|
||
|
assert index_freq == seq_freq
|
||
|
|
||
|
def test_reindex_fill_value(self):
|
||
|
df = DataFrame(np.random.randn(10, 4))
|
||
|
|
||
|
# axis=0
|
||
|
result = df.reindex(list(range(15)))
|
||
|
assert np.isnan(result.values[-5:]).all()
|
||
|
|
||
|
result = df.reindex(range(15), fill_value=0)
|
||
|
expected = df.reindex(range(15)).fillna(0)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
# axis=1
|
||
|
result = df.reindex(columns=range(5), fill_value=0.0)
|
||
|
expected = df.copy()
|
||
|
expected[4] = 0.0
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex(columns=range(5), fill_value=0)
|
||
|
expected = df.copy()
|
||
|
expected[4] = 0
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex(columns=range(5), fill_value="foo")
|
||
|
expected = df.copy()
|
||
|
expected[4] = "foo"
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
# other dtypes
|
||
|
df["foo"] = "foo"
|
||
|
result = df.reindex(range(15), fill_value=0)
|
||
|
expected = df.reindex(range(15)).fillna(0)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_dups(self):
|
||
|
|
||
|
# GH4746, reindex on duplicate index error messages
|
||
|
arr = np.random.randn(10)
|
||
|
df = DataFrame(arr, index=[1, 2, 3, 4, 5, 1, 2, 3, 4, 5])
|
||
|
|
||
|
# set index is ok
|
||
|
result = df.copy()
|
||
|
result.index = list(range(len(df)))
|
||
|
expected = DataFrame(arr, index=list(range(len(df))))
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
# reindex fails
|
||
|
msg = "cannot reindex from a duplicate axis"
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df.reindex(index=list(range(len(df))))
|
||
|
|
||
|
def test_reindex_axis_style(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/12392
|
||
|
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
||
|
expected = DataFrame(
|
||
|
{"A": [1, 2, np.nan], "B": [4, 5, np.nan]}, index=[0, 1, 3]
|
||
|
)
|
||
|
result = df.reindex([0, 1, 3])
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex([0, 1, 3], axis=0)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex([0, 1, 3], axis="index")
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_positional_warns(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/12392
|
||
|
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
||
|
expected = DataFrame({"A": [1.0, 2], "B": [4.0, 5], "C": [np.nan, np.nan]})
|
||
|
with tm.assert_produces_warning(FutureWarning):
|
||
|
result = df.reindex([0, 1], ["A", "B", "C"])
|
||
|
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_axis_style_raises(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/12392
|
||
|
df = DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex([0, 1], ["A"], axis=1)
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex([0, 1], ["A"], axis="index")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(index=[0, 1], axis="index")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(index=[0, 1], axis="columns")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(columns=[0, 1], axis="columns")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(index=[0, 1], columns=[0, 1], axis="columns")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify all"):
|
||
|
df.reindex([0, 1], [0], ["A"])
|
||
|
|
||
|
# Mixing styles
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(index=[0, 1], axis="index")
|
||
|
|
||
|
with pytest.raises(TypeError, match="Cannot specify both 'axis'"):
|
||
|
df.reindex(index=[0, 1], axis="columns")
|
||
|
|
||
|
# Duplicates
|
||
|
with pytest.raises(TypeError, match="multiple values"):
|
||
|
df.reindex([0, 1], labels=[0, 1])
|
||
|
|
||
|
def test_reindex_single_named_indexer(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/12392
|
||
|
df = DataFrame({"A": [1, 2, 3], "B": [1, 2, 3]})
|
||
|
result = df.reindex([0, 1], columns=["A"])
|
||
|
expected = DataFrame({"A": [1, 2]})
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_api_equivalence(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/12392
|
||
|
# equivalence of the labels/axis and index/columns API's
|
||
|
df = DataFrame(
|
||
|
[[1, 2, 3], [3, 4, 5], [5, 6, 7]],
|
||
|
index=["a", "b", "c"],
|
||
|
columns=["d", "e", "f"],
|
||
|
)
|
||
|
|
||
|
res1 = df.reindex(["b", "a"])
|
||
|
res2 = df.reindex(index=["b", "a"])
|
||
|
res3 = df.reindex(labels=["b", "a"])
|
||
|
res4 = df.reindex(labels=["b", "a"], axis=0)
|
||
|
res5 = df.reindex(["b", "a"], axis=0)
|
||
|
for res in [res2, res3, res4, res5]:
|
||
|
tm.assert_frame_equal(res1, res)
|
||
|
|
||
|
res1 = df.reindex(columns=["e", "d"])
|
||
|
res2 = df.reindex(["e", "d"], axis=1)
|
||
|
res3 = df.reindex(labels=["e", "d"], axis=1)
|
||
|
for res in [res2, res3]:
|
||
|
tm.assert_frame_equal(res1, res)
|
||
|
|
||
|
with tm.assert_produces_warning(FutureWarning) as m:
|
||
|
res1 = df.reindex(["b", "a"], ["e", "d"])
|
||
|
assert "reindex" in str(m[0].message)
|
||
|
res2 = df.reindex(columns=["e", "d"], index=["b", "a"])
|
||
|
res3 = df.reindex(labels=["b", "a"], axis=0).reindex(labels=["e", "d"], axis=1)
|
||
|
for res in [res2, res3]:
|
||
|
tm.assert_frame_equal(res1, res)
|
||
|
|
||
|
def test_reindex_boolean(self):
|
||
|
frame = DataFrame(
|
||
|
np.ones((10, 2), dtype=bool), index=np.arange(0, 20, 2), columns=[0, 2]
|
||
|
)
|
||
|
|
||
|
reindexed = frame.reindex(np.arange(10))
|
||
|
assert reindexed.values.dtype == np.object_
|
||
|
assert isna(reindexed[0][1])
|
||
|
|
||
|
reindexed = frame.reindex(columns=range(3))
|
||
|
assert reindexed.values.dtype == np.object_
|
||
|
assert isna(reindexed[1]).all()
|
||
|
|
||
|
def test_reindex_objects(self, float_string_frame):
|
||
|
reindexed = float_string_frame.reindex(columns=["foo", "A", "B"])
|
||
|
assert "foo" in reindexed
|
||
|
|
||
|
reindexed = float_string_frame.reindex(columns=["A", "B"])
|
||
|
assert "foo" not in reindexed
|
||
|
|
||
|
def test_reindex_corner(self, int_frame):
|
||
|
index = Index(["a", "b", "c"])
|
||
|
dm = DataFrame({}).reindex(index=[1, 2, 3])
|
||
|
reindexed = dm.reindex(columns=index)
|
||
|
tm.assert_index_equal(reindexed.columns, index)
|
||
|
|
||
|
# ints are weird
|
||
|
smaller = int_frame.reindex(columns=["A", "B", "E"])
|
||
|
assert smaller["E"].dtype == np.float64
|
||
|
|
||
|
def test_reindex_with_nans(self):
|
||
|
df = DataFrame(
|
||
|
[[1, 2], [3, 4], [np.nan, np.nan], [7, 8], [9, 10]],
|
||
|
columns=["a", "b"],
|
||
|
index=[100.0, 101.0, np.nan, 102.0, 103.0],
|
||
|
)
|
||
|
|
||
|
result = df.reindex(index=[101.0, 102.0, 103.0])
|
||
|
expected = df.iloc[[1, 3, 4]]
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex(index=[103.0])
|
||
|
expected = df.iloc[[4]]
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
result = df.reindex(index=[101.0])
|
||
|
expected = df.iloc[[1]]
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_multi(self):
|
||
|
df = DataFrame(np.random.randn(3, 3))
|
||
|
|
||
|
result = df.reindex(index=range(4), columns=range(4))
|
||
|
expected = df.reindex(list(range(4))).reindex(columns=range(4))
|
||
|
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
df = DataFrame(np.random.randint(0, 10, (3, 3)))
|
||
|
|
||
|
result = df.reindex(index=range(4), columns=range(4))
|
||
|
expected = df.reindex(list(range(4))).reindex(columns=range(4))
|
||
|
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
df = DataFrame(np.random.randint(0, 10, (3, 3)))
|
||
|
|
||
|
result = df.reindex(index=range(2), columns=range(2))
|
||
|
expected = df.reindex(range(2)).reindex(columns=range(2))
|
||
|
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
df = DataFrame(np.random.randn(5, 3) + 1j, columns=["a", "b", "c"])
|
||
|
|
||
|
result = df.reindex(index=[0, 1], columns=["a", "b"])
|
||
|
expected = df.reindex([0, 1]).reindex(columns=["a", "b"])
|
||
|
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_multi_categorical_time(self):
|
||
|
# https://github.com/pandas-dev/pandas/issues/21390
|
||
|
midx = pd.MultiIndex.from_product(
|
||
|
[
|
||
|
Categorical(["a", "b", "c"]),
|
||
|
Categorical(date_range("2012-01-01", periods=3, freq="H")),
|
||
|
]
|
||
|
)
|
||
|
df = DataFrame({"a": range(len(midx))}, index=midx)
|
||
|
df2 = df.iloc[[0, 1, 2, 3, 4, 5, 6, 8]]
|
||
|
|
||
|
result = df2.reindex(midx)
|
||
|
expected = DataFrame({"a": [0, 1, 2, 3, 4, 5, 6, np.nan, 8]}, index=midx)
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
def test_reindex_with_categoricalindex(self):
|
||
|
df = DataFrame(
|
||
|
{
|
||
|
"A": np.arange(3, dtype="int64"),
|
||
|
},
|
||
|
index=CategoricalIndex(list("abc"), dtype=CDT(list("cabe")), name="B"),
|
||
|
)
|
||
|
|
||
|
# reindexing
|
||
|
# convert to a regular index
|
||
|
result = df.reindex(["a", "b", "e"])
|
||
|
expected = DataFrame({"A": [0, 1, np.nan], "B": Series(list("abe"))}).set_index(
|
||
|
"B"
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["a", "b"])
|
||
|
expected = DataFrame({"A": [0, 1], "B": Series(list("ab"))}).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["e"])
|
||
|
expected = DataFrame({"A": [np.nan], "B": Series(["e"])}).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["d"])
|
||
|
expected = DataFrame({"A": [np.nan], "B": Series(["d"])}).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
# since we are actually reindexing with a Categorical
|
||
|
# then return a Categorical
|
||
|
cats = list("cabe")
|
||
|
|
||
|
result = df.reindex(Categorical(["a", "e"], categories=cats))
|
||
|
expected = DataFrame(
|
||
|
{"A": [0, np.nan], "B": Series(list("ae")).astype(CDT(cats))}
|
||
|
).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(Categorical(["a"], categories=cats))
|
||
|
expected = DataFrame(
|
||
|
{"A": [0], "B": Series(list("a")).astype(CDT(cats))}
|
||
|
).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["a", "b", "e"])
|
||
|
expected = DataFrame({"A": [0, 1, np.nan], "B": Series(list("abe"))}).set_index(
|
||
|
"B"
|
||
|
)
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["a", "b"])
|
||
|
expected = DataFrame({"A": [0, 1], "B": Series(list("ab"))}).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(["e"])
|
||
|
expected = DataFrame({"A": [np.nan], "B": Series(["e"])}).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
# give back the type of categorical that we received
|
||
|
result = df.reindex(Categorical(["a", "e"], categories=cats, ordered=True))
|
||
|
expected = DataFrame(
|
||
|
{"A": [0, np.nan], "B": Series(list("ae")).astype(CDT(cats, ordered=True))}
|
||
|
).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
result = df.reindex(Categorical(["a", "d"], categories=["a", "d"]))
|
||
|
expected = DataFrame(
|
||
|
{"A": [0, np.nan], "B": Series(list("ad")).astype(CDT(["a", "d"]))}
|
||
|
).set_index("B")
|
||
|
tm.assert_frame_equal(result, expected, check_index_type=True)
|
||
|
|
||
|
df2 = DataFrame(
|
||
|
{
|
||
|
"A": np.arange(6, dtype="int64"),
|
||
|
},
|
||
|
index=CategoricalIndex(list("aabbca"), dtype=CDT(list("cabe")), name="B"),
|
||
|
)
|
||
|
# passed duplicate indexers are not allowed
|
||
|
msg = "cannot reindex from a duplicate axis"
|
||
|
with pytest.raises(ValueError, match=msg):
|
||
|
df2.reindex(["a", "b"])
|
||
|
|
||
|
# args NotImplemented ATM
|
||
|
msg = r"argument {} is not implemented for CategoricalIndex\.reindex"
|
||
|
with pytest.raises(NotImplementedError, match=msg.format("method")):
|
||
|
df.reindex(["a"], method="ffill")
|
||
|
with pytest.raises(NotImplementedError, match=msg.format("level")):
|
||
|
df.reindex(["a"], level=1)
|
||
|
with pytest.raises(NotImplementedError, match=msg.format("limit")):
|
||
|
df.reindex(["a"], limit=2)
|
||
|
|
||
|
def test_reindex_signature(self):
|
||
|
sig = inspect.signature(DataFrame.reindex)
|
||
|
parameters = set(sig.parameters)
|
||
|
assert parameters == {
|
||
|
"self",
|
||
|
"labels",
|
||
|
"index",
|
||
|
"columns",
|
||
|
"axis",
|
||
|
"limit",
|
||
|
"copy",
|
||
|
"level",
|
||
|
"method",
|
||
|
"fill_value",
|
||
|
"tolerance",
|
||
|
}
|
||
|
|
||
|
def test_reindex_multiindex_ffill_added_rows(self):
|
||
|
# GH#23693
|
||
|
# reindex added rows with nan values even when fill method was specified
|
||
|
mi = MultiIndex.from_tuples([("a", "b"), ("d", "e")])
|
||
|
df = DataFrame([[0, 7], [3, 4]], index=mi, columns=["x", "y"])
|
||
|
mi2 = MultiIndex.from_tuples([("a", "b"), ("d", "e"), ("h", "i")])
|
||
|
result = df.reindex(mi2, axis=0, method="ffill")
|
||
|
expected = DataFrame([[0, 7], [3, 4], [3, 4]], index=mi2, columns=["x", "y"])
|
||
|
tm.assert_frame_equal(result, expected)
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"kwargs",
|
||
|
[
|
||
|
{"method": "pad", "tolerance": timedelta(seconds=9)},
|
||
|
{"method": "backfill", "tolerance": timedelta(seconds=9)},
|
||
|
{"method": "nearest"},
|
||
|
{"method": None},
|
||
|
],
|
||
|
)
|
||
|
def test_reindex_empty_frame(self, kwargs):
|
||
|
# GH#27315
|
||
|
idx = date_range(start="2020", freq="30s", periods=3)
|
||
|
df = DataFrame([], index=Index([], name="time"), columns=["a"])
|
||
|
result = df.reindex(idx, **kwargs)
|
||
|
expected = DataFrame({"a": [pd.NA] * 3}, index=idx)
|
||
|
tm.assert_frame_equal(result, expected)
|