Python 删除行中的重复值,替换为NaN,将NaN移到行的末尾
问题: 如何从每一行中删除重复的单元格值,在一个数据帧中分别考虑每一行(也许用NAN替换它们) 如果我们能将所有新创建的NAN移到每行的末尾,那就更好了Python 删除行中的重复值,替换为NaN,将NaN移到行的末尾,python,pandas,dataframe,duplicates,Python,Pandas,Dataframe,Duplicates,问题: 如何从每一行中删除重复的单元格值,在一个数据帧中分别考虑每一行(也许用NAN替换它们) 如果我们能将所有新创建的NAN移到每行的末尾,那就更好了 参考:相关但不同的帖子: 在上发布如何删除被视为重复的整行: 在上发布如何从列中的列表中删除重复项: (该答案返回一系列字符串,而不是数据帧) 例如: import pandas as pd df = pd.DataFrame({'a': ['A', 'A', 'C', 'B'],
参考:相关但不同的帖子:
- 在上发布如何删除被视为重复的整行:
- 在上发布如何从列中的列表中删除重复项:
-
- (该答案返回一系列字符串,而不是数据帧)
-
例如:
import pandas as pd
df = pd.DataFrame({'a': ['A', 'A', 'C', 'B'],
'b': ['B', 'D', 'B', 'B'],
'c': ['C', 'C', 'C', 'A'],
'd': ['D', 'D', 'B', 'A']},
index=[0, 1, 2, 3])
这将创建此df
:
A.
B
C
D
0
A.
B
C
D
1.
A.
D
C
D
2.
C
B
C
B
3.
B
B
A.
A.
您可以
堆叠
,然后以这种方式删除重复的
。然后,我们需要借助于cumcount
级别的帮助进行旋转。stack
保留值沿行的显示顺序,cumcount
确保NaN
将显示在最后
df1 = df.stack().reset_index().drop(columns='level_1').drop_duplicates()
df1['col'] = df1.groupby('level_0').cumcount()
df1 = (df1.pivot(index='level_0', columns='col', values=0)
.rename_axis(index=None, columns=None))
0 1 2 3
0 A B C D
1 A D C NaN
2 C B NaN NaN
3 B A NaN NaN
时间安排 假设有4列,让我们看看随着行数的增加,这些方法之间的比较情况。当事情很小时,
map
和apply
解决方案有很好的优势,但是随着数据帧变长,它们比更复杂的堆栈
+drop\u duplicates
+pivot
解决方案慢一些。不管怎样,它们都开始需要一段时间才能获得一个大数据帧
import perfplot
import pandas as pd
import numpy as np
def stack(df):
df1 = df.stack().reset_index().drop(columns='level_1').drop_duplicates()
df1['col'] = df1.groupby('level_0').cumcount()
df1 = (df1.pivot(index='level_0', columns='col', values=0)
.rename_axis(index=None, columns=None))
return df1
def apply_drop_dup(df):
return pd.DataFrame.from_dict(df.apply(lambda x: x.drop_duplicates().tolist(),
axis=1).to_dict(), orient='index')
def apply_unique(df):
return pd.DataFrame(df.apply(pd.Series.unique, axis=1).tolist())
def list_map(df):
return pd.DataFrame(list(map(pd.unique, df.values)))
perfplot.show(
setup=lambda n: pd.DataFrame(np.random.choice(list('ABCD'), (n, 4)),
columns=list('abcd')),
kernels=[
lambda df: stack(df),
lambda df: apply_drop_dup(df),
lambda df: apply_unique(df),
lambda df: list_map(df),
],
labels=['stack', 'apply_drop_dup', 'apply_unique', 'list_map'],
n_range=[2 ** k for k in range(18)],
equality_check=lambda x,y: x.compare(y).empty,
xlabel='~len(df)'
)
最后,如果保留值最初在每行中出现的顺序并不重要,则可以使用
numpy
。要消除重复,请进行排序,然后检查差异。然后创建将值向右移动的输出数组。由于此方法将始终返回4列,因此在每行的唯一值少于4个的情况下,我们需要一个dropna
来匹配其他输出
def with_numpy(df):
arr = np.sort(df.to_numpy(), axis=1)
r = np.roll(arr, 1, axis=1)
r[:, 0] = np.NaN
arr = np.where((arr != r), arr, np.NaN)
# Move all NaN to the right. Credit @Divakar
mask = pd.notnull(arr)
justified_mask = np.flip(np.sort(mask, axis=1), 1)
out = np.full(arr.shape, np.NaN, dtype=object)
out[justified_mask] = arr[mask]
return pd.DataFrame(out, index=df.index).dropna(how='all', axis='columns')
with_numpy(df)
# 0 1 2 3
#0 A B C D
#1 A C D NaN
#2 B C NaN NaN # B/c this method sorts, B before C
#3 A B NaN NaN
您可以在
行
轴上搜索重复项,然后通过使用特定键对结果进行排序,将结果“推”到行末尾的Nan
:
duplicates = df.apply(pd.Series.duplicated, axis=1)
df.where(~duplicates, np.nan).apply(lambda x: pd.Series(sorted(x, key=pd.isnull)), axis=1)
输出
| 0 | 1 | 2 | 3 |
|:----|:----|:----|:----|
| A | B | C | D |
| A | D | C | NaN |
| C | B | NaN | NaN |
| B | A | NaN | NaN |
尝试新事物
df = pd.DataFrame(list(map(pd.unique, df.values)))
Out[447]:
0 1 2 3
0 A B C D
1 A D C None
2 C B None None
3 B A None None
使用
apply
并通过pd.dataframe.from\u dict
选项orient='index'
df_final = pd.DataFrame.from_dict(df.apply(lambda x: x.drop_duplicates().tolist(),
axis=1).to_dict(), orient='index')
Out[268]:
0 1 2 3
0 A B C D
1 A D C None
2 C B None None
3 B A None None
注:
None
实际上类似于NaN
。如果您想要精确的NaN
。只需在每行上链接附加的.fillna(np.nan)
应用pd.Series.unique
,提取结果并重新构建数据框:
print (pd.DataFrame(df.apply(pd.Series.unique, axis=1).tolist()))
0 1 2 3
0 A B C D
1 A D C None
2 C B None None
3 B A None None
我试图找到一个问题,这篇文章是重复的,但令我惊讶的是,我没有找到任何一个是一个好的适合重复。我欢迎任何关于重复项的建议。你从未定义过“重复项”的含义,特别是因为你使用的是与正常意义不同的含义(“多列中的不同值,按行考虑”),并且你的标题过于宽泛,人们会被谷歌错误地发送到这里,因此搜索。举例来说,在第1行,为什么不从列“C”中删除第二个“C”,但从列“D”中删除了第二个“D”?这毫无意义。此外,“用NaN替换单元格”并不是真正的“删除”,因此这是一个两个问题,代码解决方案将不同(
fillna
,duplicated
,drop\u duplicates
,等等)。在您的用例中,您有多个列,所有列都被认为是等效的,相同的数据类型,不关心名称。因此,“FirstName=Murphy,LastName=Brown”将被视为“Brown,Murphy”的“复制品”;或者zipcode为77024、收入为60001或客户id为45678都将被视为“等同于”列中相同值的其他排列。这绝对不是“重复”的标准定义。您的数据实际上只是一个数组,而不是一个真正的数据帧,“将所有新创建的NaN移到每行的末尾”这一部分就证明了这一点。我不理解这个问题:“在第1行,为什么您没有从列“C”中删除第二个“C”,但您确实从列“D”中删除了第二个“D”第一行只有一个C。关于其他的:我希望你的编辑能够解决这些问题&这篇文章现在可以了。谢谢!这确实有效。如果有人提出了一个更简单的解决方案,那么这个问题就留一点余地吧。如果没有,我们肯定会接受这个问题。@Alolz:谢谢perfplot
。我真的很好奇每个解决方案的性能,但我不擅长设置它。我已经投了你的票。否则,我会在perfplot
:)上再次向上投票
print (pd.DataFrame(df.apply(pd.Series.unique, axis=1).tolist()))
0 1 2 3
0 A B C D
1 A D C None
2 C B None None
3 B A None None