Python 使用某一对平衡行的数量
我有一个熊猫数据框,看起来像这样:Python 使用某一对平衡行的数量,python,pandas,Python,Pandas,我有一个熊猫数据框,看起来像这样: data | Flag | Set ----------------------------- 0 | True | A 30 | True | A -1 | False | A 20 | True | B 5 | False | B 19 | False | B 7 | False | C 8 | False | c 我
data | Flag | Set
-----------------------------
0 | True | A
30 | True | A
-1 | False | A
20 | True | B
5 | False | B
19 | False | B
7 | False | C
8 | False | c
我怎样才能优雅地删除行,使每个集合都有相同数量的真标志和假标志?输出将如下所示
data | Flag | Set
-----------------------------
0 | True | A
-1 | False | A
20 | True | B
5 | False | B
对于A,有一个假标志,对于B,有一个真标志,对于C,有零个真标志。我知道如何强制执行此操作,但我觉得有一种优雅的方式我不知道。首先获取每个集合的标志计数,过滤掉带有0的行-这意味着唯一的真值或假值,并将最小值输入字典d:
然后通过设置字典的列和键过滤行,然后通过此命令使用每组:
编辑:对于验证返回的解决方案,如果每个集合有2倍的True和False A:
首先获取每个集合的标志计数,筛选出具有0的行-它表示唯一的真值或假值,并获取字典d的最小值:
然后通过设置字典的列和键过滤行,然后通过此命令使用每组:
编辑:对于验证返回的解决方案,如果每个集合有2倍的True和False A:
编辑:我提出了一个简单易懂的解决方案: 只需将.cumcount按集合和标志分组即可 检查代码中cc上方的一组集合和cumcount结果是否重复。如果组不包含重复项,则意味着需要将其删除。 编辑2:Per@Jezrael,我可以进一步简化以下三行代码:
df = (df[df.assign(cc = df.groupby(['Set', 'Flag'])
.cumcount()).duplicated(['Set','cc'], keep=False)])
代码的进一步细分如下
在删除之前,数据的外观如下所示:
df['cc'] = df.groupby(['Set', 'Flag']).cumcount()
df['s'] = df.duplicated(['Set','cc'], keep=False)
# df = df[df['s']].drop('cc', axis=1)
df
Out[1]:
data Flag Set cc s
0 0 True A 0 True
1 8 True A 1 True
2 30 True A 2 True
3 0 True A 3 True
4 8 True A 4 False
5 30 True A 5 False
6 -1 False A 0 True
7 -14 False A 1 True
8 -1 False A 2 True
9 -14 False A 3 True
10 20 True B 0 True
11 5 False B 0 True
12 19 False B 1 False
13 7 False C 0 False
14 8 False c 0 False
然后,使用df=df[df['s']]编辑删除s列中的错误行:我想出了一个简单易懂的解决方案: 只需将.cumcount按集合和标志分组即可 检查代码中cc上方的一组集合和cumcount结果是否重复。如果组不包含重复项,则意味着需要将其删除。 编辑2:Per@Jezrael,我可以进一步简化以下三行代码:
df = (df[df.assign(cc = df.groupby(['Set', 'Flag'])
.cumcount()).duplicated(['Set','cc'], keep=False)])
代码的进一步细分如下
在删除之前,数据的外观如下所示:
df['cc'] = df.groupby(['Set', 'Flag']).cumcount()
df['s'] = df.duplicated(['Set','cc'], keep=False)
# df = df[df['s']].drop('cc', axis=1)
df
Out[1]:
data Flag Set cc s
0 0 True A 0 True
1 8 True A 1 True
2 30 True A 2 True
3 0 True A 3 True
4 8 True A 4 False
5 30 True A 5 False
6 -1 False A 0 True
7 -14 False A 1 True
8 -1 False A 2 True
9 -14 False A 3 True
10 20 True B 0 True
11 5 False B 0 True
12 19 False B 1 False
13 7 False C 0 False
14 8 False c 0 False
然后,使用df=df[df['s']]删除列s中的假行。这可能是一个由3个步骤组成的解决方案: 删除此处没有真标志和假标志的所有集合C 计算每个集合标志组合所需的行数 删除超过该计数行数的所有行 这将产生以下代码:
df = pd.DataFrame(data={"data":[0, 30, -1, 20, 5, 19, 7, 8],
"Flag":[True, True, False, True, False, False, False, False],
"Set":["A", "A", "A", "B", "B", "B", "C", "C"]})
# 1. removing sets with only one of both flags
reducer = df.groupby("Set")["Flag"].transform("nunique") > 1
df_reduced = df.loc[reducer]
# 2. counting the minimum number of rows per set
counts = df_reduced.groupby(["Set", "Flag"]).count().groupby("Set").min()
# 3. reducing each set and flag to the minumum number of rows
df_equal = df_reduced.groupby(["Set", "Flag"]) \
.apply(lambda x: x.head(counts.loc[x["Set"].values[0]][0])) \
.reset_index(drop=True)
这可能是一个可能的解决方案,包括3个步骤: 删除此处没有真标志和假标志的所有集合C 计算每个集合标志组合所需的行数 删除超过该计数行数的所有行 这将产生以下代码:
df = pd.DataFrame(data={"data":[0, 30, -1, 20, 5, 19, 7, 8],
"Flag":[True, True, False, True, False, False, False, False],
"Set":["A", "A", "A", "B", "B", "B", "C", "C"]})
# 1. removing sets with only one of both flags
reducer = df.groupby("Set")["Flag"].transform("nunique") > 1
df_reduced = df.loc[reducer]
# 2. counting the minimum number of rows per set
counts = df_reduced.groupby(["Set", "Flag"]).count().groupby("Set").min()
# 3. reducing each set and flag to the minumum number of rows
df_equal = df_reduced.groupby(["Set", "Flag"]) \
.apply(lambda x: x.head(counts.loc[x["Set"].values[0]][0])) \
.reset_index(drop=True)
也许我实现得很差,但这似乎删除了比我预期的更多的行。比如说,对于某个特定的群体,我可能有30个正确答案,25个错误答案。明白了@Warlax56,我只是在评论中问了这个问题。我会删除这个答案。@Warlax56如果你有兴趣,我已经更新了我的解决方案,虽然耶兹雷尔的答案更简洁。最简单的是df=df[df.assigncc=df.groupby['Set','Flag'].cumcount.duplicated['Set','cc'],keep=False]我试图进一步简化它,但我没有想到这一点。非常感谢。也许我实现得很差,但这似乎删除了比我预期的更多的行。比如说,对于某个特定的群体,我可能有30个正确答案,25个错误答案。明白了@Warlax56,我只是在评论中问了这个问题。我会删除这个答案。@Warlax56如果你有兴趣,我已经更新了我的解决方案,虽然耶兹雷尔的答案更简洁。最简单的是df=df[df.assigncc=df.groupby['Set','Flag'].cumcount.duplicated['Set','cc'],keep=False]我试图进一步简化它,但我没有想到这一点。非常感谢。你会希望每个集合只有一个True和一个False吗?或者如果你有更多的数据,你会希望一个集合有10个True和10个False,如果有11个False和10个True?你会希望每个集合只有一个True和一个False吗?或者如果你有更多的数据,例如,如果有11个假和10个真,你会想要一组10个真和10个假?耶兹雷尔的回答肯定是花哨的,但我确实认为这是优雅和简单之间的一个很好的平衡耶兹雷尔的回答肯定是花哨的,但我确实认为这是优雅和简单之间的一个很好的平衡
df['cc'] = df.groupby(['Set', 'Flag']).cumcount()
df['s'] = df.duplicated(['Set','cc'], keep=False)
# df = df[df['s']].drop('cc', axis=1)
df
Out[1]:
data Flag Set cc s
0 0 True A 0 True
1 8 True A 1 True
2 30 True A 2 True
3 0 True A 3 True
4 8 True A 4 False
5 30 True A 5 False
6 -1 False A 0 True
7 -14 False A 1 True
8 -1 False A 2 True
9 -14 False A 3 True
10 20 True B 0 True
11 5 False B 0 True
12 19 False B 1 False
13 7 False C 0 False
14 8 False c 0 False
df = pd.DataFrame(data={"data":[0, 30, -1, 20, 5, 19, 7, 8],
"Flag":[True, True, False, True, False, False, False, False],
"Set":["A", "A", "A", "B", "B", "B", "C", "C"]})
# 1. removing sets with only one of both flags
reducer = df.groupby("Set")["Flag"].transform("nunique") > 1
df_reduced = df.loc[reducer]
# 2. counting the minimum number of rows per set
counts = df_reduced.groupby(["Set", "Flag"]).count().groupby("Set").min()
# 3. reducing each set and flag to the minumum number of rows
df_equal = df_reduced.groupby(["Set", "Flag"]) \
.apply(lambda x: x.head(counts.loc[x["Set"].values[0]][0])) \
.reset_index(drop=True)