如果其他地方存在其他“更好”的值,则替换dataframe列中的值

时间:2018-06-16 17:20:36

标签: python python-2.7 pandas dataframe

我的数据框结构大致如下(它是一个事件参与者列表;池足够小,我们可以假设重复值指的是同一个人):

id_1 id_2 id_3 ... year  name   country
1_c  2_a  3_a      2011  John   France
1_b  2_a  3_c      2010  Jill   UK
1_c  2_b  3_c      2018  John   Germany
1_c  2_b  3_c      2014  Jason  Italy
1_c  2_b  3_b      2017  John   Unknown

目的是将“未知”值替换为国家/地区名称,如果该人员在另一年参与其中已知国家/地区。

在极不可能的事件中,他们在不同年份的不同国家/地区上市,我很高兴在最接近“未知”年份的年份中将其列入其中的任何国家(因此,我们将John的'Unknown'改为'Germany'。

我是一个完整的熊猫(和python!)新手。我使用drop_duplicates创建了一个唯一的名称/国家/地区对的列表,但我假设必须有一个更优雅的方式来完成其余的工作,而不是我现在膝盖深处的列表,元组和字典转换的混乱。

2 个答案:

答案 0 :(得分:2)

通过pd.DataFrame.apply可以实现一个非向量化解决方案。这只是一个薄薄的循环。我们循环每一行。如果这个国家不为人知我们:

  • 过滤country不等于"未知"和name等于行名。
  • 计算此子集的每年与行年之间的绝对差异。
  • 检索country最小绝对年差。

这是一个完整的例子:

def get_country(row):
    if row['country'] != 'Unknown':
        return row['country']
    else:
        res = df.loc[(df['country'] != 'Unknown') & (df['name'] == row['name'])]\
                .assign(year_diff=(df['year']-row['year']).abs())
        return res.loc[res['year_diff'].idxmin(), 'country'] if not res.empty else 'Unknown'

df['country'] = df.apply(get_country, axis=1)

print(df)

  id_1 id_2 id_3  year   name  country
0  1_c  2_a  3_a  2011   John   France
1  1_b  2_a  3_c  2010   Jill       UK
2  1_c  2_b  3_c  2018   John  Germany
3  1_c  2_b  3_c  2014  Jason    Italy
4  1_c  2_b  3_b  2017   John  Germany

肯定会有一些聪明的方法来优化使用Pandas / NumPy,例如通过排序。如果考虑性能,则应考虑替代算法。

答案 1 :(得分:0)

这是一种可能性。我认为由于申请,它可能仍然处于缓慢的一方,但如果你有少量独特的名称与行数相比可能会更快:

  • Unknown替换为np.NaN
  • 对DataFrame进行排序,并将索引设置为' year'
  • 我们会创建一个字典,用于映射' country'中的所有字符串。到数值。
  • 这样您就可以使用pd.Series.interpolate(method='nearest')
    • 如果它是第一个条目或最后一个条目,并且如果一切都是NaN
    • ,则需要在几个案例中填写正确填写
  • 插值后,将值映射回来。

以下是代码:

import pandas as pd
import numpy as np

df = df.replace('Unknown', np.NaN)
df = df.sort_values(['name', 'year']).set_index('year')

dct = dict(zip(df.country[df.country.notnull()].unique(), 
               range(df.country[df.country.notnull()].nunique())))
inv_dct = {v: k for k, v in dct.items()}
df['country'] = df['country'].map(dct)

df['country'] = df.groupby('name')['country'].apply(
                   lambda x: x.interpolate(method='nearest').bfill().ffill()
                             if x.notnull().sum() > 1 else x.bfill().ffill())

df['country'] = df['country'].map(inv_dct)

输出:

     id_1 id_2 id_3   name  country
year                               
2014  1_c  2_b  3_c  Jason    Italy
2010  1_b  2_a  3_c   Jill       UK
2011  1_c  2_a  3_a   John   France
2017  1_c  2_b  3_b   John  Germany
2018  1_c  2_b  3_c   John  Germany