Pandas复制一行以填充DataFrame

3

我卡在死胡同里了,而且在使用一些明显不适合 Pandas 的代码来完成应该很简单的任务。我相信肯定有更好的方法。

我有一个 DataFrame,我将从中提取一行并创建一个新的 DataFrame,如下所示:

>>> sampledata
float_col  int_col str_col  r    v  new_coltest      eddd
0        0.1        1       a  5  1.0          0.1 -0.539783
1        0.2        2       b  5  NaN          0.2 -1.394550
2        0.2        6    None  5  NaN          0.2  0.290157
3       10.1        8       c  5  NaN         10.1 -1.799373
4        NaN       -1       a  5  NaN          NaN  0.694682
>>> newsampledata = sampledata[(sampledata.new_coltest == 0.1) & (sampledata.float_col == 0.1)]
>>> newsampledata
float_col  int_col str_col  r    v  new_coltest      eddd
0        0.1        1       a  5  1.0          0.1 -0.539783

我想做的是将“newsampledata”中的单行复制n次,其中n是已知整数。理想情况下,具有n行的最终DataFrame将覆盖单行“newsampledata”,但这并不重要。
我目前正在使用for循环执行pd.concat n-1次以填充DataFrame,但由于concat的工作方式,这不是快速的。我还尝试过使用append来尝试相同类型的策略,但这比concat稍慢。
我看到了一些关于类似项目的其他问题,但许多人之前没有见过这个确切的问题。此外,由于性能问题,我已被引导远离map/apply,但如果您在该方法中看到了良好的性能,请告诉我,我也会尝试。
4个回答

6

我想你可以使用替换方法来进行抽样

newsampledata.sample(n, replace=True).reset_index(drop=True)

或者使用reindex函数。
newsampledata.reindex(newsampledata.index.repeat(n)).reset_index(drop=True)

4
你可以使用 DataFrame 构造函数:
N = 10
df =pd.DataFrame(newsampledata.values.tolist(),index=np.arange(N),columns=sampledata.columns)
print (df)
   float_col  int_col str_col  r    v  new_coltest      eddd
0        0.1        1       a  5  1.0          0.1 -0.539783
1        0.1        1       a  5  1.0          0.1 -0.539783
2        0.1        1       a  5  1.0          0.1 -0.539783
3        0.1        1       a  5  1.0          0.1 -0.539783
4        0.1        1       a  5  1.0          0.1 -0.539783
5        0.1        1       a  5  1.0          0.1 -0.539783
6        0.1        1       a  5  1.0          0.1 -0.539783
7        0.1        1       a  5  1.0          0.1 -0.539783
8        0.1        1       a  5  1.0          0.1 -0.539783
9        0.1        1       a  5  1.0          0.1 -0.539783

print (df.dtypes)
float_col      float64
int_col          int64
str_col         object
r                int64
v              float64
new_coltest    float64
eddd           float64
dtype: object

时间:

在大型的DataFrame构造方法中,小型的DataFrame使用samplereindex方法更快。

N = 1000
In [88]: %timeit (pd.DataFrame(newsampledata.values.tolist(), index=np.arange(N), columns=sampledata.columns))
1000 loops, best of 3: 745 µs per loop

In [89]: %timeit (newsampledata.sample(N, replace=True).reset_index(drop=True))
The slowest run took 4.88 times longer than the fastest. This could mean that an intermediate result is being cached.
1000 loops, best of 3: 470 µs per loop

In [90]: %timeit (newsampledata.reindex(newsampledata.index.repeat(N)).reset_index(drop=True))
1000 loops, best of 3: 476 µs per loop

N = 10000
In [92]: %timeit (pd.DataFrame(newsampledata.values.tolist(), index=np.arange(N), columns=sampledata.columns))
1000 loops, best of 3: 946 µs per loop

In [93]: %timeit (newsampledata.sample(N, replace=True).reset_index(drop=True))
1000 loops, best of 3: 775 µs per loop

In [94]: %timeit (newsampledata.reindex(newsampledata.index.repeat(N)).reset_index(drop=True))
1000 loops, best of 3: 827 µs per loop

N = 100000
In [97]: %timeit (pd.DataFrame(newsampledata.values.tolist(), index=np.arange(N), columns=sampledata.columns))
The slowest run took 12.98 times longer than the fastest. This could mean that an intermediate result is being cached.
100 loops, best of 3: 6.93 ms per loop

In [98]: %timeit (newsampledata.sample(N, replace=True).reset_index(drop=True))
100 loops, best of 3: 7.07 ms per loop

In [99]: %timeit (newsampledata.reindex(newsampledata.index.repeat(N)).reset_index(drop=True))
100 loops, best of 3: 7.87 ms per loop

N = 10000000
In [83]: %timeit (pd.DataFrame(newsampledata.values.tolist(), index=np.arange(N), columns=sampledata.columns))
1 loop, best of 3: 589 ms per loop

In [84]: %timeit (newsampledata.sample(N, replace=True).reset_index(drop=True))
1 loop, best of 3: 757 ms per loop

In [85]: %timeit (newsampledata.reindex(newsampledata.index.repeat(N)).reset_index(drop=True))
1 loop, best of 3: 731 ms per loop

好的解决方案,似乎没有问题,并且我同意,它更快。不知道如何设置索引,得记住这个! - rajan
在之前的版本中,您使用了一个numpy版本,但缺点是dtypes被转换为对象。当转换回原始数据类型时,这种解决方案在性能上如何比较?也许numpy仍然更快 ;) - Quickbeam2k1
1
@Quickbeam2k1 - 我试试看。 - jezrael
1
@Quickbeam2k1 - 我试了一下,速度非常慢。In [108]: %timeit numpy_tile(newsampledata,10000000) 1 loop, best of 3: 7.42 s per loop - jezrael
感谢您的努力!不知道pandas是如何实现这个的。也许,我会在今天晚些时候检查源代码。 - Quickbeam2k1

2
我认为你可以使用concat而不需要显式地使用for循环。
df = pd.DataFrame({'a':[1], 'b':[.1]})
repetitions = 4
res = pd.concat([df]*repetitions)
print(res)

输出

   a    b
0  1  0.1
0  1  0.1
0  1  0.1
0  1  0.1

在我的样本框架上,使用这种方法确实比使用for循环快大约5倍。然而,我期望不使用concat的其他解决方案会更快。
为了展示concat有多慢,与jezrael的一个解决方案进行了比较,下面是一些基准测试结果: enter image description here

concat操作非常慢。一个只有一行的数据框,在n=10,000时需要1.5秒。 - Ted Petrou
你是对的。然而,这个解决方案至少比直接使用for循环要快。 - Quickbeam2k1
进行了一些基准测试,以展示拼接操作的速度慢,与Jezraels的解决方案相比。 - Quickbeam2k1

1

这是数以亿计的方法之一

pd.concat([df.query('new_coltest == 0.1 & float_col == 0.1')] * 4)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接