Python 熊猫:根据条件更改单元格值

Python 熊猫:根据条件更改单元格值,python,pandas,Python,Pandas,我有以下数据框 import pandas as pd data = {'id_a': [1, 1, 1, 2, 2, 2, 3, 4], 'name_a': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'd'], 'id_b': [5, 6, 7, 8, 9, 10, 11, 11], 'name_b': ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'k'], 'similar': [1, 1, 1,

我有以下数据框

import pandas as pd

data = {'id_a': [1, 1, 1, 2, 2, 2, 3, 4], 'name_a': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'd'], 
        'id_b': [5, 6, 7, 8, 9, 10, 11, 11], 'name_b': ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'k'], 
        'similar': [1, 1, 1, 1, 1, 0, 1, 1], 'metric': [.5, 1, .8, .7, .2, .9, .8, .9]}
df = pd.DataFrame(data)
print(df)

在此表中,A组的ID基于列相似性链接到B组的ID

但我需要每个组的唯一ID,以便只对应于另一个组的一个ID

在每个组具有相同ID的行中,我需要选择列度量最大的行

例如,我有三行id_a==2。在这三行中,只有两行的列相似值等于1。 在这两行中,一行的列度量值为0.7,第二行的列度量值为0.2

我将column Simular的值保留为1,仅针对列度量为0.7的行,因为它是最大值,而对于第二行,我将column Simular的值保留为0

也就是说,我需要以下数据帧:

output_data = {'id_a': [1, 1, 1, 2, 2, 2, 3, 4], 'name_a': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'd'], 
               'id_b': [5, 6, 7, 8, 9, 10, 11, 11], 'name_b': ['e', 'f', 'g', 'h', 'i', 'j', 'k', 'k'], 
               'similar': [0, 1, 0, 1, 0, 0, 0, 1], 'metric': [.5, 1, .8, .7, .2, .9, .8, .9]}
output_df = pd.DataFrame(output_data)
print(output_df)


问题:如何使用Python实现这一点,因为我的研究没有给出任何结果?

我不确定您如何处理Instance的id_a==3的情况,但我认为这是您想要的。只需从id_a分组的每个组中获取最大索引,然后在重置类似列后,将这些最大索引重置为1

max_vals = df.groupby('id_a').apply(lambda grp: grp.loc[grp['similar'] == 1, 'metric'].idxmax())
df['similar'] = 0
df.loc[max_vals, 'similar'] = 1

>>> df

    id_a    name_a  id_b    name_b  similar metric
0   1       a       5       e       0       0.5
1   1       a       6       f       1       1.0
2   1       a       7       g       0       0.8
3   2       b       8       h       1       0.7
4   2       b       9       i       0       0.2
5   2       b       10      j       0       0.9
6   3       c       11      k       1       0.8
7   4       d       11      k       1       0.9

编辑:查看有关为什么输出与第6行不完全匹配的注释。

仅使用矢量化方法的解决方案

m1:每组最大值的向量,类似==1 m2:相似==1的行 m3:具有最大值和类似值==1的行 IIUC,你可以做:

# find the indices of the maximum by id_a
keep_a = df[df.similar.eq(1)].groupby('id_a').filter(lambda x: len(x) > 1).groupby('id_a').metric.idxmax()

# find the indices of the maximum by id_b
keep_b = df[df.similar.eq(1)].groupby('id_b').filter(lambda x: len(x) > 1).groupby('id_b').metric.idxmax()

# create mask False if is in set of maximum
mask = ~df.index.isin(set(keep_a) | set(keep_b))

# set values using mask
df.loc[mask, 'similar'] = 0

print(df)
输出

这里有一个明确、对称、有序和快速的方法来完成这项任务。 将metric的值转换为NaN,其中simular==0,这样它就永远不会是最大值,因此结果中有一个1

++当存在连续值时,能够进行分组​​在id_a或id_b中。请记住,对于N个ID,这将是如此简单

使用groupby.transform按组创建具有最大值的序列,并将其与度量序列进行比较,以获得可转换为1或0的布尔序列

输出

团体详情

在listcomp中的2个groupby上使用groupby idxmax、isin和,并传递到np.array。最后,在np.array上调用all和astype



为什么第6行相似变为0?因为有两行id_b==11第6行和第7行,第7行度量值大于第6行度量值,所以还必须考虑id_b?@DanielMesejo,yes@lemon,但他们不是在不同的船上,因为他们有不同的身份证吗?你能解释一下我们在两列之间比较度量的约束吗?这个解决方案不等于预期的输出。我认为我们可以为id_b重新运行这个脚本。只要将groupby'id_a'更改为groupby'id_b',我们就会收到预期的输出。嗯,我认为这是一个输入错误,同时考虑到其他答案的输出@DanielMesejoyeah,这是我的错-我最初没有描述id_bOP的情况,如果您需要在id_b上再次运行它,那么您需要确保每个组至少有一个相似值,即1:df.groupby'id_b'。filterlambda grp:1 in grp['Simular']。值。那么你应该能够运行它两次。这不是OP提供的预期输出。而且这似乎效率低下。如果有更多ID,这里会发生什么?你说的更多ID是什么意思?例如,第三列id_c?我想说更多id和更多名称,而不仅仅是A和b ie N而不是2,您是否需要分组N次?@ansev我相信是的,但我没有解决方法。此输出与预期不符,您没有考虑id_b@ansev当前位置我是在离开电脑的路上想到这个的。我很快地看了看另一个投票率高的答案来比较我的结果,所以我没有注意到。无论如何,我编辑了答案来解释id_b:我认为正确的事情是正确的和可扩展的解决方案得到了奖励:@ansev:谢谢。你的也不错。我不明白为什么有人否决了你的。我把你的票提高了:+1
max_vals = df.groupby('id_a').apply(lambda grp: grp.loc[grp['similar'] == 1, 'metric'].idxmax())
df['similar'] = 0
df.loc[max_vals, 'similar'] = 1

>>> df

    id_a    name_a  id_b    name_b  similar metric
0   1       a       5       e       0       0.5
1   1       a       6       f       1       1.0
2   1       a       7       g       0       0.8
3   2       b       8       h       1       0.7
4   2       b       9       i       0       0.2
5   2       b       10      j       0       0.9
6   3       c       11      k       1       0.8
7   4       d       11      k       1       0.9
m1 = df.query('similar == 1').groupby('id_a')['metric'].transform('max')
m2 = df['similar'].eq(1)
m3 = df.loc[m2, 'metric'].eq(m1)

df.loc[m3[~m3].index, 'similar'] = 0
   id_a name_a  id_b name_b  similar  metric
0     1      a     5      e        0    0.50
1     1      a     6      f        1    1.00
2     1      a     7      g        0    0.80
3     2      b     8      h        1    0.70
4     2      b     9      i        0    0.20
5     2      b    10      j        0    0.90
6     3      c    11      k        1    0.80
7     4      d    11      k        1    0.90
# find the indices of the maximum by id_a
keep_a = df[df.similar.eq(1)].groupby('id_a').filter(lambda x: len(x) > 1).groupby('id_a').metric.idxmax()

# find the indices of the maximum by id_b
keep_b = df[df.similar.eq(1)].groupby('id_b').filter(lambda x: len(x) > 1).groupby('id_b').metric.idxmax()

# create mask False if is in set of maximum
mask = ~df.index.isin(set(keep_a) | set(keep_b))

# set values using mask
df.loc[mask, 'similar'] = 0

print(df)
   id_a name_a  id_b name_b  similar  metric
0     1      a     5      e        0     0.5
1     1      a     6      f        1     1.0
2     1      a     7      g        0     0.8
3     2      b     8      h        1     0.7
4     2      b     9      i        0     0.2
5     2      b    10      j        0     0.9
6     3      c    11      k        0     0.8
7     4      d    11      k        1     0.9
df2=df.copy()
#discarding similar == 0 as a maximum candidate in the groups
df2['metric']=df2['metric'].mask(df2['similar'].eq(0))

#creating groups depend on id_a and id_b
ids=df2[['id_a','id_b']]
groups=ids.ne(ids.shift()).all(axis=1).cumsum()

#checking the maximum per group and converting to integer
df['similar']=df['metric'].eq(df2.groupby(groups).metric.transform('max')).astype(int)
print(df)
   id_a name_a  id_b name_b  similar  metric
0     1      a     5      e        0     0.5
1     1      a     6      f        1     1.0
2     1      a     7      g        0     0.8
3     2      b     8      h        1     0.7
4     2      b     9      i        0     0.2
5     2      b    10      j        0     0.9
6     3      c    11      k        0     0.8
7     4      d    11      k        1     0.9
print(groups)
0    1
1    1
2    1
3    2
4    2
5    2
6    3
7    3
dtype: int64
df1 = df[df.similar.eq(1)]
df['similar'] = np.array([df.index.isin(df1.groupby(col).metric.idxmax()) 
                            for col in ['id_a', 'id_b']]).all(0).astype(int)


Out[132]:
   id_a name_a  id_b name_b  similar  metric
0     1      a     5      e        0     0.5
1     1      a     6      f        1     1.0
2     1      a     7      g        0     0.8
3     2      b     8      h        1     0.7
4     2      b     9      i        0     0.2
5     2      b    10      j        0     0.9
6     3      c    11      k        0     0.8
7     4      d    11      k        1     0.9