熊猫只删除连续重复的行,忽略特定的列
Posted
技术标签:
【中文标题】熊猫只删除连续重复的行,忽略特定的列【英文标题】:Pandas drop consecutive duplicate rows only, ignoring specific columns 【发布时间】:2020-11-09 12:57:22 【问题描述】:我在下面有一个数据框
df = pd.DataFrame(
'ID': ['James', 'James', 'James', 'James',
'Max', 'Max', 'Max', 'Max', 'Max',
'Park', 'Park','Park', 'Park',
'Tom', 'Tom', 'Tom', 'Tom'],
'From_num': [578, 420, 420, 'Started', 298, 78, 36, 298, 'Started', 28, 28, 311, 'Started', 60, 520, 99, 'Started'],
'To_num': [96, 578, 578, 420, 36, 298, 78, 36, 298, 112, 112, 28, 311, 150, 60, 520, 99],
'Date': ['2020-05-12', '2020-02-02', '2020-02-01', '2019-06-18',
'2019-08-26', '2019-06-20', '2019-01-30', '2018-10-23',
'2018-08-29', '2020-05-21', '2020-05-20', '2019-11-22',
'2019-04-12', '2019-10-16', '2019-08-26', '2018-12-11', '2018-10-09'])
它是这样的:
ID From_num To_num Date
0 James 578 96 2020-05-12
1 James 420 578 2020-02-02
2 James 420 578 2020-02-01 # Drop the this duplicated row (ignore date)
3 James Started 420 2019-06-18
4 Max 298 36 2019-08-26
5 Max 78 298 2019-06-20
6 Max 36 78 2019-01-30
7 Max 298 36 2018-10-23
8 Max Started 298 2018-08-29
9 Park 28 112 2020-05-21
10 Park 28 112 2020-05-20 # Drop this duplicate row (ignore date)
11 Park 311 28 2019-11-22
12 Park Started 311 2019-04-12
13 Tom 60 150 2019-10-16
14 Tom 520 60 2019-08-26
15 Tom 99 520 2018-12-11
16 Tom Started 99 2018-10-09
每个“ID”(名称)中都有一些连续的重复值(忽略日期值),例如James 的第 1 行和第 2 行,From_num 都是 420,与第 9 行和第 10 行相同,我希望删除第二个重复行并保留第一行。我写了循环条件,但它非常冗余和缓慢,我认为可能有更简单的方法可以做到这一点,所以如果你有想法请帮忙。万分感谢。预期的结果是这样的:
ID From_num To_num Date
0 James 578 96 2020-05-12
1 James 420 578 2020-02-02
2 James Started 420 2019-06-18
3 Max 298 36 2019-08-26
4 Max 78 298 2019-06-20
5 Max 36 78 2019-01-30
6 Max 298 36 2018-10-23
7 Max Started 298 2018-08-29
8 Park 28 112 2020-05-21
9 Park 311 28 2019-11-22
10 Park Started 311 2019-04-12
11 Tom 60 150 2019-10-16
12 Tom 520 60 2019-08-26
13 Tom 99 520 2018-12-11
14 Tom Started 99 2018-10-09
【问题讨论】:
感谢@sammywemmy,不,不幸的是,这也会删除 Max 组中的重复行之一,但它不是连续的,我希望保留它。只删除连续的重复值 【参考方案1】:有点晚了,但这是否符合您的要求?这会忽略“日期”而丢弃连续重复。
t = df[['ID', 'From_num', 'To_num']]
df[(t.ne(t.shift())).any(axis=1)]
ID From_num To_num Date
0 James 578 96 2020-05-12
1 James 420 578 2020-02-02
3 James Started 420 2019-06-18
4 Max 298 36 2019-08-26
5 Max 78 298 2019-06-20
6 Max 36 78 2019-01-30
7 Max 298 36 2018-10-23
8 Max Started 298 2018-08-29
9 Park 28 112 2020-05-21
11 Park 311 28 2019-11-22
12 Park Started 311 2019-04-12
13 Tom 60 150 2019-10-16
14 Tom 520 60 2019-08-26
15 Tom 99 520 2018-12-11
16 Tom Started 99 2018-10-09
这会删除索引值为 2 和 10 的行。
【讨论】:
嘿@cs95,这太完美了。我用我的冗余代码解决了这些问题,但是您的代码非常高效且快速,我将其更改为您的代码。干杯【参考方案2】:将下面的行与上面的行进行比较,反转布尔值以获得结果:
cond1 = df.ID.eq(df.ID.shift())
cond2 = df.From_num.eq(df.From_num.shift())
cond = cond1 & cond2
df.loc[~cond].reset_index(drop=True)
替代方案:更长的路线:
(
df.assign(
temp=df.groupby(["ID", "From_num"]).From_num.transform("size"),
check=lambda x: (x.From_num.eq(x.From_num.shift())) &
(x.temp.eq(x.temp.shift())),
)
.query("check == 0")
.drop(["temp", "check"], axis=1)
)
【讨论】:
【参考方案3】:在我看来这正是DataFrame.drop_duplicates
所做的,默认情况下它会保留第一次出现并丢弃其余的
unique_df = df.drop_duplicates(['ID', 'From_num', 'To_num'])
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop_duplicates.html
编辑
正如问题中提到的,只应处理连续行,为此我建议先标记它们,然后在标记行的子集上运行 drop_duplicates(我不确定这是否是最佳解决方案)
df['original_index'] = null
indices = df.index[1:]
for i in range(1, indices):
# if current row equals the previous one
if df.loc[indices[i - 1], 'ID'] == df.loc[indices[i], 'ID'] and df.loc[indices[i -1], 'From_num'] == df.loc[indices[i], 'From_num'] and df.loc[indices[i -1], 'To_num'] == df.loc[indices[i], 'To_num']:
# get the original index if it has been already set on row index -1
if df.loc[indices[i - 1], 'original_index'] not null:
df.loc[indices[i], 'original_index'] = df.loc[indices[i - 1], 'original_index']
else:
# else set it to be current index for both rows
df.loc[indices[i - 1], 'original_index'] = indices[i - 1]
df.loc[indices[i], 'original_index'] = indices[i - 1]
现在我们将列 'original_index' 添加到 drop_duplicates
unique_df = df.drop_duplicates(['ID', 'From_num', 'To_num', 'original_index'])
【讨论】:
感谢@Hicham Zouarhi,您的结果还删除了“Max”组中的一个非连续行,请参见第 4 行和第 7 行,它们是同一行但不连续。正如我在标题中提到的那样,我只想删除每个组中的 CONSECUTIVE 重复行 @Alicejinx 对不起,我没看到,我会更新我的答案【参考方案4】:df.groupby(['ID', 'From_num', 'To_num']).first().reset_index()
编辑 - 这将删除重复项,即使它们不连续。例如原始 df 中的第 4 行和第 7 行。
更新
cols=['ID', 'From_num', 'To_num']
df.loc[(df[cols].shift() != df[cols]).any(axis=1)].shape
【讨论】:
谢谢@Tom Ron,重复值已被删除,但原始顺序也已更改。我希望保持相同的顺序 第 4 行和第 7 行呢,是否应该删除其中的一个? 没错,我想保留它们,因为我只想删除连续的重复值以上是关于熊猫只删除连续重复的行,忽略特定的列的主要内容,如果未能解决你的问题,请参考以下文章
如何告诉 git 忽略个别行,即特定代码行的 gitignore [重复]