【问题标题】:Pandas Dataframe - filter data to get unique maximum and minimum rowsPandas Dataframe - 过滤数据以获得唯一的最大和最小行
【发布时间】:2018-08-11 20:39:25
【问题描述】:

我有一个包含这 4 个数字列的数据框:['ID', 'A', 'B', 'C']

我想过滤数据以获得一个数据框,其中对于列 ID 中的每个唯一值,我得到行,不重复,它们对应于 A 列的最大值和最小值,B,C

下图显示了输入数据帧和所需的输出数据帧。

我还报告了 df #2,以蓝色突出显示与简单的最大/最小搜索不同的行。因为其中一些是重复的,然后应该替换为第二/第三..最大/最小行。

例如,df2 的第三行替换为B (63) 列中包含第二个最大值的行,即df1 的第三行。同理,将df2的第四行替换为df1的第四行,因为它包含B列的第二个最小值(-75)

此外:

  1. 列数可以改变,这意味着在更大的问题中,我可以有更多的列,而不仅仅是 ['A']['B']['C']

  2. ID的行数可以改变

  3. df3的总行数应该是UniqueID*Columns*2

目前我只能使用 idxmax() / idxmin()reindex 获取 df2 数据帧

df1 = pd.DataFrame({'ID': pd.Series([1. ,1. , 1. , 1  , 2 , 2, 2,2,2,2,2]),
   'A': pd.Series([100. , -97. , -56. , 69  , 150 , -120, 30,92,35,-41,-75]),
   'B': pd.Series([99., -96., 63., -75., 140, -110, 91,-62,76,10,2]),
   'C': pd.Series([98., -95., -45., 39., 130, -100,90,-50,70,-17,33])})



max = df1.groupby('ID')['A', 'B','C'].idxmax().as_matrix()
min = df1.groupby('ID')['A', 'B','C'].idxmin().as_matrix()

index = []
for i in range(len(max)):
    for j in range(len(max[0])):
        index.append(max[i][j])
        index.append(min[i][j])

df2 = df1.reindex(index)

我怎样才能获得 df3?数据框很大(>1M 行),所以我不仅需要一个有效的解决方案,还需要一个高效的解决方案。

【问题讨论】:

  • 我似乎无法理解您要做什么。
  • 基本上我需要为列 ID 中的每个唯一值找到在列 P、D 和 C 中具有最大值的行。然后重新创建仅包含最大行的数据框。但我需要用第二个最大值替换重复的行
  • 所以对于每个唯一 ID,您需要 3 行,即最大 P、最大 D 和最大 C 的行?
  • 是的,我已经能够使用 idmax() 做到这一点。但这给了我一些重复的行。例如当 D 的最大值的行与 P 相同时
  • 那么 ID 0 也有重复的行,那你为什么要保持原样呢?

标签: python pandas dataframe replace duplicates


【解决方案1】:

有一种只保留唯一行的快速方法:df3 = df1.reindex(set(index))。这将只保留第一个最大值。现在您可以从 df1df1 = df1.drop(df3.index) 删除具有第一个最大值的行,并根据需要重复整个过程(例如 3 次)

import pandas as pd
df1 = pd.DataFrame({'ID': pd.Series([1. ,1. , 1. , 1  , 2 , 2, 2,2,2,2,2]),
   'A': pd.Series([100. , -97. , -56. , 69  , 150 , -120, 30,92,35,-41,-75]),
   'B': pd.Series([99., -96., 63., -75., 140, -110, 91,-62,76,10,2]),
   'C': pd.Series([98., -95., -45., 39., 130, -100,90,-50,70,-17,33])})

def keep_minmax(df1):
    df_max = df1.groupby('ID')['A', 'B','C'].idxmax().as_matrix()
    df_min = df1.groupby('ID')['A', 'B','C'].idxmin().as_matrix()
    index = []
    for i in range(len(df_max)):
        for j in range(len(df_max[0])):
            index.append(df_max[i][j])
            index.append(df_min[i][j])
    return df1.reindex(set(index))

df = df1.copy()
results = []
for i in range(3):
    result = keep_minmax(df)
    result['order'] = i + 1
    results.append(result)
    df = df.drop(result.index)
df3 = pd.concat(results).sort_values(['ID', 'order'])
print(df3)

它会输出

        A      B      C   ID  order
0   100.0   99.0   98.0  1.0      1
1   -97.0  -96.0  -95.0  1.0      1
2   -56.0   63.0  -45.0  1.0      2
3    69.0  -75.0   39.0  1.0      2
4   150.0  140.0  130.0  2.0      1
5  -120.0 -110.0 -100.0  2.0      1
6    30.0   91.0   90.0  2.0      2
7    92.0  -62.0  -50.0  2.0      2
10  -75.0    2.0   33.0  2.0      2
8    35.0   76.0   70.0  2.0      3
9   -41.0   10.0  -17.0  2.0      3

您可以看到对于ID=1没有第三顺序,因为 df1 中的所有行都已经用尽,您必须包含重复的行(如您的示例 @987654327 @)。 你真的想要吗?

我问这个,因为从您的帖子中不清楚 在模棱两可的情况下该怎么做:如果不同的行对应于不同列中的k'th 最佳值,或者如果这个@ 987654329@本身对于不同的列是不同的。例如,你会从这样的df 中产生什么样的df3,为什么?为简单起见,我们只提取最大值:

   A  B   ID
0  2  1  1.0
1  3  2  1.0
2  1  0  1.0
3  0  3  1.0

我的算法(只寻找最大值)会返回

   A  B   ID  order
1  3  2  1.0      1
3  0  3  1.0      1
0  2  1  1.0      2
2  1  0  1.0      3

请注意,第 (2, 1) 行是 A 的第 2 行,B 的第 3 行,因为它更高,所以被包含在第 2 位中。

您对如何处理此类歧义有其他建议吗?

【讨论】:

  • 您好,感谢您的回答。是的,如果替代行结束,我真的需要包含重复的行。输出数据帧的行应该是 'uniqueID*column*2'。您目前的示例总共给出了 11 行,“ID=1”为 4 行,“ID=2”为 7 行,而不是 6+6。
  • 对于模棱两可的情况,算法应该独立搜索每列内的最大值。第二个示例的结果应如下所示: 'pd.DataFrame({'ID': pd.Series([1., 1.]), 'A': pd.Series([3., 0.]) , 'B': pd.Series([2., 3.])})'。我不介意结果数据框中是否有两个“3”,重要的是行不同
  • 为什么会出现这个结果?我真的不明白其中的逻辑
  • 因为算法应该在A列中搜索最大值,即第一行的3,将(3,2)行保存为结果数据框的第一行。然后在第 3 行 B 列中搜索最大值,即 3 并将行 (0,3) 保存为结果数据帧的第二行。
【解决方案2】:

使用辅助功能:

def filter_min_and_max(x):
    y = pd.DataFrame()
    for col in x.columns:
        if col != "ID":
            y[col] = [max(x[col]), min(x[col])]
            # for OP's comment
            y[col] = [val_1 for val in zip(x[col].nlargest(3).tolist(), x[col].nsmallest(3).tolist()) for val_1 in val]
    return y

df1.groupby("ID").apply(lambda x: filter_min_and_max(x)).reset_index().drop(["level_1"], axis=1)

【讨论】:

  • 嗨,这个解决方案给出了一个“TypeError: 'numpy.ndarray' object is not callable”
  • @tuocuggino,首先,对不起,我错误地在答案末尾写了一个“1”,现在我将其删除。你初始化df1 喜欢你的问题吗?我没有收到任何错误(删除最后一个字符 1 时)。
  • 我解决了错误,我使用的是 pandas 0.20.2,这是正确的语法 df1.groupby("ID").apply(lambda x: filter_min_and_max(x)).reset_index().drop(["level_1"], axis=1) 无论如何,使用您的解决方案我得到 df3 但没有蓝色行,输出中有 4 行而不是 12。
  • @tuocuggino,我无法理解替换df3 中重复行的逻辑。如果要保留除最大值和最小值之外的元素,可以将y[col] = [max(x[col]), min(x[col])] 更改为y[col] = [max(x[col]), min(x[col])] + list(x[col][~x[col].isin([max(x[col]), min(x[col])])])
  • 你能解释一下你的疑惑吗?我会尽力澄清他们。我在主帖中添加了一个示例,也许会有所帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-30
  • 2021-09-15
  • 2018-10-09
  • 2014-02-19
  • 1970-01-01
  • 2017-11-07
相关资源
最近更新 更多