如何复制 Pandas 数据帧的行?



我的熊猫数据帧如下所示:

Person  ID   ZipCode   Gender
0  12345   882  38182     Female
1  32917   271  88172     Male
2  18273   552  90291     Female

我想复制每一行 3 次并重置索引以获得:

Person  ID   ZipCode   Gender
0  12345   882  38182     Female
1  12345   882  38182     Female
2  12345   882  38182     Female
3  32917   271  88172     Male
4  32917   271  88172     Male
5  32917   271  88172     Male
6  18273   552  90291     Female
7  18273   552  90291     Female
8  18273   552  90291     Female

我尝试了以下解决方案:

pd.concat([df[:5]]*3, ignore_index=True)

和:

df.reindex(np.repeat(df.index.values, df['ID']), method='ffill')

但他们都没有奏效。

解决方案:

使用np.repeat

版本1:

尝试使用np.repeat

newdf = pd.DataFrame(np.repeat(df.values, 3, axis=0))
newdf.columns = df.columns
print(newdf)

上面的代码将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

np.repeat重复df的值,3次。

然后我们添加带有分配new_df.columns = df.columns的列。

版本2:

您还可以在第一行中分配列名称,如下所示:

newdf = pd.DataFrame(np.repeat(df.values, 3, axis=0), columns=df.columns)
print(newdf)

上面的代码也将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

版本3:

您可以使用loc缩短它,只重复索引,如下所示:

newdf = df.loc[np.repeat(df.index, 3)].reset_index(drop=True)
print(newdf)

上面的代码也将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

我使用reset_index将索引替换为单调索引 (0, 1, 2, 3, 4...(。

没有np.repeat

版本4:

您可以使用内置的pd.Index.repeat函数,如下所示:

newdf = df.loc[df.index.repeat(3)].reset_index(drop=True)
print(newdf)

上面的代码也将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

记得添加reset_index来排列index

版本5:

或者将concatsort_index一起使用,如下所示:

newdf = pd.concat([df] * 3).sort_index().reset_index(drop=True)
print(newdf)

上面的代码也将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

版本6:

你也可以将loc与 Python 一起使用list乘法和sorted,如下所示:

newdf = df.loc[sorted([*df.index] * 3)].reset_index(drop=True)
print(newdf)

上面的代码也将输出:

Person   ID ZipCode  Gender
0  12345  882   38182  Female
1  12345  882   38182  Female
2  12345  882   38182  Female
3  32917  271   88172    Male
4  32917  271   88172    Male
5  32917  271   88172    Male
6  18273  552   90291  Female
7  18273  552   90291  Female
8  18273  552   90291  Female

计时:

使用以下代码进行计时:

import timeit
import pandas as pd
import numpy as np
df = pd.DataFrame({'Person': {0: 12345, 1: 32917, 2: 18273}, 'ID': {0: 882, 1: 271, 2: 552}, 'ZipCode': {0: 38182, 1: 88172, 2: 90291}, 'Gender': {0: 'Female', 1: 'Male', 2: 'Female'}})
def version1():
newdf = pd.DataFrame(np.repeat(df.values, 3, axis=0))
newdf.columns = df.columns

def version2():
newdf = pd.DataFrame(np.repeat(df.values, 3, axis=0), columns=df.columns)

def version3():
newdf = df.loc[np.repeat(df.index, 3)].reset_index(drop=True)

def version4():
newdf = df.loc[df.index.repeat(3)].reset_index(drop=True)

def version5():
newdf = pd.concat([df] * 3).sort_index().reset_index(drop=True)

def version6():
newdf = df.loc[sorted([*df.index] * 3)].reset_index(drop=True)

print('Version 1 Speed:', timeit.timeit('version1()', 'from __main__ import version1', number=20000))
print('Version 2 Speed:', timeit.timeit('version2()', 'from __main__ import version2', number=20000))
print('Version 3 Speed:', timeit.timeit('version3()', 'from __main__ import version3', number=20000))
print('Version 4 Speed:', timeit.timeit('version4()', 'from __main__ import version4', number=20000))
print('Version 5 Speed:', timeit.timeit('version5()', 'from __main__ import version5', number=20000))
print('Version 6 Speed:', timeit.timeit('version6()', 'from __main__ import version6', number=20000))

输出:

Version 1 Speed: 9.879425965991686
Version 2 Speed: 7.752138633004506
Version 3 Speed: 7.078321029010112
Version 4 Speed: 8.01169377300539
Version 5 Speed: 19.853051771002356
Version 6 Speed: 9.801617017001263

我们可以看到版本 2 和 3 比其他版本更快,原因是因为它们都使用np.repeat函数,而numpy函数非常快,因为它们是用 C 实现的。

版本3 由于使用了loc而不是DataFrame,因此略微战胜了版本 2。

版本 5 由于函数concatsort_index,速度明显较慢,因为concat复制DataFrame二次,这需要更长的时间。

最快版本:版本 3。

这些将重复索引并保留列,如 op 所示

iloc版本 1

df.iloc[np.arange(len(df)).repeat(3)]

iloc版本 2

df.iloc[np.arange(len(df) * 3) // 3]

使用concat

pd.concat([df]*3).sort_index()
Out[129]: 
Person   ID  ZipCode  Gender
0   12345  882    38182  Female
0   12345  882    38182  Female
0   12345  882    38182  Female
1   32917  271    88172    Male
1   32917  271    88172    Male
1   32917  271    88172    Male
2   18273  552    90291  Female
2   18273  552    90291  Female
2   18273  552    90291  Female

我不确定为什么从未提出过,但您可以轻松地将df.index.repeat.loc进行猜想:

new_df = df.loc[df.index.repeat(3)]

输出:

>>> new_df
Person   ID  ZipCode  Gender
0   12345  882    38182  Female
0   12345  882    38182  Female
0   12345  882    38182  Female
1   32917  271    88172    Male
1   32917  271    88172    Male
1   32917  271    88172    Male
2   18273  552    90291  Female
2   18273  552    90291  Female
2   18273  552    90291  Female

您可以尝试以下代码:

df = df.iloc[df.index.repeat(3),:].reset_index()

df.index.repeat(3)将创建一个列表,其中每个索引值将重复 3 次,df.iloc[df.index.repeat(3),:]将帮助生成一个数据帧,其中包含此列表完全返回的行。

你可以这样做。

def do_things(df, n_times):
ndf = df.append(pd.DataFrame({'name' : np.repeat(df.name.values, n_times) }))
ndf = ndf.sort_values(by='name')
ndf = ndf.reset_index(drop=True)
return ndf
if __name__ == '__main__':
df = pd.DataFrame({'name' : ['Peter', 'Quill', 'Jackson']}) 
n_times = 3
print do_things(df, n_times)

并解释...

import pandas as pd
import numpy as np
n_times = 3
df = pd.DataFrame({'name' : ['Peter', 'Quill', 'Jackson']})
#       name
# 0    Peter
# 1    Quill
# 2  Jackson
#   Duplicating data.
df = df.append(pd.DataFrame({'name' : np.repeat(df.name.values, n_times) }))
#       name
# 0    Peter
# 1    Quill
# 2  Jackson
# 0    Peter
# 1    Peter
# 2    Peter
# 3    Quill
# 4    Quill
# 5    Quill
# 6  Jackson
# 7  Jackson
# 8  Jackson
#   The DataFrame is sorted by 'name' column.
df = df.sort_values(by=['name'])
#       name
# 2  Jackson
# 6  Jackson
# 7  Jackson
# 8  Jackson
# 0    Peter
# 0    Peter
# 1    Peter
# 2    Peter
# 1    Quill
# 3    Quill
# 4    Quill
# 5    Quill
#   Reseting the index.
#   You can play with drop=True and drop=False, as parameter of `reset_index()`
df = df.reset_index()
#     index     name
# 0       2  Jackson
# 1       6  Jackson
# 2       7  Jackson
# 3       8  Jackson
# 4       0    Peter
# 5       0    Peter
# 6       1    Peter
# 7       2    Peter
# 8       1    Quill
# 9       3    Quill
# 10      4    Quill
# 11      5    Quill

如果您需要为重复编制索引(例如,对于多索引(,并且还要根据列中的值来设置重复次数,则可以这样做:

someDF["RepeatIndex"] = someDF["RepeatBasis"].fillna(value=0).apply(lambda x: list(range(int(x))) if x > 0 else [])
superDF = someDF.explode("RepeatIndex").dropna(subset="RepeatIndex")

这给出了一个数据帧,其中每条记录都是重复的,但在"重复基础"列中指示了多少次。 数据帧还获取一个"RepeatIndex"列,您可以将其与现有索引组合成多索引,从而保留索引唯一性。

如果有人想知道你为什么要做这样的事情,就我而言,当我获得已经汇总频率的数据时,无论出于何种原因,我都需要使用单一的观察。 (想想对直方图进行逆向工程(

这个问题还没有足够的答案!以下是一些仍然缺失并允许链接:)的更多方法

# SQL-style cross-join
# (one line and counts replicas)
(
data
.join(pd.DataFrame(range(3), columns=["replica"]), how="cross")
.drop(columns="replica")  # remove if you want to count replicas
)
# DataFrame.apply + Series.repeat
# (most readable, but potentially slow)
(
data
.apply(lambda x: x.repeat(3))
.reset_index(drop=True)
)
# DataFrame.explode
# (fun to have explosions in your code)
(
data
.assign(replica=lambda df: [[x for x in range(3)]] * len(df))
.explode("replica", ignore_index=True)
.drop(columns="replica")  # or keep if you want to know which copy it is
)

(编辑:更严重的是,如果您需要对副本进行计数并且每行具有动态副本计数,则使用explode很有用。例如,如果您有具有开始日期和结束日期的每客户使用情况数据,则可以使用上述数据将数据转换为每月每个客户的使用情况数据。


当然,这里是创建用于测试的数据的代码片段:

data = pd.DataFrame([
[12345, 882, 38182, "Female"],
[32917, 271, 88172, "Male"],
[18273, 552, 90291, "Female"],
],
columns=["Person", "ID", "ZipCode", "Gender"]
)

使用pd.concat:创建三个相同的数据帧并将它们合并在一起,不使用大量代码:

df = pd.concat([df]*3, ignore_index=True)
print(df)
Person  ID   ZipCode   Gender
0  12345   882  38182     Female
1  12345   882  38182     Female
2  12345   882  38182     Female
3  32917   271  88172     Male
4  32917   271  88172     Male
5  32917   271  88172     Male
6  18273   552  90291     Female
7  18273   552  90291     Female
8  18273   552  90291     Female

注意:ignore_index=True使索引重置。

也可以使用np.tile()

df.loc[np.tile(df.index,3)].sort_index().reset_index(drop=True)

输出:

Person   ID  ZipCode  Gender
0   12345  882    38182  Female
1   12345  882    38182  Female
2   12345  882    38182  Female
3   32917  271    88172    Male
4   32917  271    88172    Male
5   32917  271    88172    Male
6   18273  552    90291  Female
7   18273  552    90291  Female
8   18273  552    90291  Female

最新更新