假设我有以下三个系列重叠索引
s1 = pd.Series(data=np.arange(5))
s2 = pd.Series(data=np.arange(5),index=np.arange(2,7))
s3 = pd.Series(data=np.arange(5),index=np.arange(5,10))
我希望将它们连接成一个系列;但是,我希望从具有“最新”索引的系列中获取过度索引中的数据值。
因此在玩具箱中,输出将是:
0 0
1 1
2 0
3 1
4 2
5 0
6 1
7 2
8 3
9 4
dtype: int32
这也可以被看作是在与下一个系列重叠并且然后连接时切割每个系列。有什么快速有效的方法可以在pandas
做到这一点,当可能有很多系列的长度。
编辑
我正在寻找一种有效的方法来实现这一点,因为实际上系列的长度和数量都很大,分别约为100k和10k。
想法是使用concatenate
用于展平指数和Series
的值,并通过Series.duplicated
用~
和倒置掩码过滤:
def new1(series):
b = [x.index for x in series]
v = np.concatenate(series)
i = np.concatenate(b)
mask = ~pd.Series(i).duplicated(keep='last')
return pd.Series(v[mask], index=i[mask])
我建议的最快速度如下:
series = [s1, s2, s3]
s = pd.concat(series)
your_series = s[(~s.index[::-1].duplicated())[::-1]]
你可以比较时间:
import functools
def method1(series):
s = pd.concat(series)
return s[(~s.index[::-1].duplicated())[::-1]]
def method2(series):
s1,s2,s3 = series
return functools.reduce(pd.Series.combine_first, [s3,s2,s1])
def method3(series):
s1,s2,s3 = series
listc = s3.append(s2).append(s1).reset_index().drop_duplicates(
subset='index', keep='first').set_index('index').sort_index()
return listc
def method4(series):
return pd.DataFrame(series).ffill().tail(1).T
结果:
>>> %timeit method1(series)
... 643 µs ± 25.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit method2(series)
... 1.15 ms ± 26.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit method3(series)
... 3.09 ms ± 262 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit method4(series)
... 1.07 ms ± 16.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
当然,在应用于更大的数据集时,这些方法的内存含义可能值得考虑。
编辑:
对更大系列的小测试:
series = [pd.Series(data=np.arange(100000), index=np.arange(i*5000,100000+i*5000)) for i in range(100)]
结果:
>>> %timeit method1(series)
... 583 ms ± 18.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit method2(series)
... 4.5 s ± 25.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
但是,当系列数较小但每个系列的尺寸较大时,差异要小得多:
series = [pd.Series(data=np.arange(1000000), index=np.arange(i*5000,1000000+i*5000)) for i in range(10)]
结果:
>>> %timeit method1(series)
... 679 ms ± 23.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit method2(series)
... 1.39 s ± 26.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
对于比这更大的任何东西,你可能想要考虑一种不同的方法,比如map-reduce作为一种可能性。
这应该可以解决问题。让我知道:
listc = s3.append(s2).append(s1).reset_index().drop_duplicates(subset='index', keep='first').set_index('index').sort_index()
这是我的看法:
# create a dataframe with all series
df = pd.DataFrame({'s1':s1, 's2':s2, 's3': s3})
# ffill for the latest index:
df.ffill(1)
输出(您的预期是最后一列):
s1 s2 s3
-- ---- ---- ----
0 0 0 0
1 1 1 1
2 2 0 0
3 3 1 1
4 4 2 2
5 nan 3 0
6 nan 4 1
7 nan nan 2
8 nan nan 3
9 nan nan 4
一个班轮:
df = pd.DataFrame([s1,s2,s3]).ffill().tail(1).T
输出:
2
-- ---
0 0
1 1
2 0
3 1
4 2
5 0
6 1
7 2
8 3
9 4
与functools.reduce
和pd.Series.combine_first
import functools
functools.reduce(pd.Series.combine_first,[s3,s2,s1])
Out[794]:
0 0.0
1 1.0
2 0.0
3 1.0
4 2.0
5 0.0
6 1.0
7 2.0
8 3.0
9 4.0
dtype: float64