【问题标题】:Adding columns to a pandas dataframe based in external criteria根据外部条件向 pandas 数据框添加列
【发布时间】:2021-03-02 06:07:36
【问题描述】:

给定三个数据框,一个包含用户数据,第二个包含数据分箱,第三个是类别名称,如下所示:

klasses_df = pd.DataFrame([[1, 'Sad'],
                           [7, 'Regular'],
                           [13, 'Happy'],
                           [42, 'Magical']],
                           columns=['klass', 'mood'])
                           
bins_df = pd.DataFrame([[0.0, 3.0, 1],
                        [3.0, 6.0, 7],
                        [6.0, 8.0, 13]],
                       columns=['lower', 'upper', 'klass'])


person_df = pd.DataFrame([['John', 1.5],
                          ['Mary', 3.6],
                          ['Paul', 7.2],
                          ['Josh', 5.7],
                          ['Phil', 9.9]],
                         columns=['name', 'feeling'])

我想扩展person_df(或创建一个新的数据框),在那里可以找到正确的klass_idmood。例如在person_df 的第一行,John 的感觉在1.5,检查bins_df 我们可以看到它在范围第一范围[0, 3] 因此在klass 1。查看klasses_df,我们发现klass_id1Sad。这将使与 Jonh 相关的最后/新行成为John, 1.5, 1, 'Sad'

为了实现这一点,我创建了两个辅助功能:

def find_klass_from_feeling(feeling, bin_data):
    values = bin_data.values
    klass = values[(values[:,0] <= feeling) & (feeling < values[:,1])][:,2]
    if len(klass) == 0:
        return 0
    else:
        return int(klass.flatten()[0])

def find_mood_from_class(klass, klasses_data):
    if klass == 0:
        return None
    retval = klasses_df[klasses_df['klass'] == klass]['mood'].iloc[0]
    return retval

我称它们为:


final_df = person_df.copy()
klss = []
moods = []
for idx, row in person_df.iterrows():
    kls = find_klass_from_feeling(row['feeling'], bins_df)
    mood = find_mood_from_class(kls, klasses_df)
    klss.append(kls)
    moods.append(mood)
    
final_df['klass'] = klss
final_df['mood'] = moods

它有效,但似乎完全错误,因为我相信,pandas 有一些更合适的方法来处理它。我尝试使用applyapplymap 没有成功。

欢迎任何提示。

【问题讨论】:

  • 查看“非 equi 连接”,例如此处讨论:stackoverflow.com/questions/50573457/…
  • 您可以将分箱重写为不同的格式,还是必须是这个数据框(即,如果分箱可以在列表中,这更易于管理)?另外,Phil 的分数超出 bin 范围会怎样?
  • 另外,您的垃圾箱在开始和结束时具有相同的值。例如,如果一个人落在 3 号上,他们应该属于 1 号箱还是 7 号箱?
  • lower &lt;= value &lt; upper 求解器。它需要相同的值,因为无法表示限制。

标签: pandas dataframe merge


【解决方案1】:

首先这个问题有几个问题:

  • 范围包括右值还是左值?现在我假设没有,值 3 将进入 bin 1。
  • 超过 8 的值会怎样?现在我为超过 8 的所有值创建新类别“99”。

要解决您的问题,您可以使用 pandas pd.cut()

您需要将您的垃圾箱和标签作为列表:

您可以手动声明它:

bins = [0.0, 3.0, 6.0, 8.0, float("inf")]
labels = [1, 7, 13, 99]

或者如果您的数据来自外部,您可以对其进行转换:

bins = [0.0]+bins_df['upper'].to_list()+[float("inf")]
label = bins_df['klass'].to_list() + [99]

(我使用 99 作为虚拟值,表示大于 8 的值的缺失类)

现在使用pd.cut()person_df['feelings'] 分箱到类:

person_df['klass']=pd.cut(person_df['feeling'], bins = bins, labels=labels)

然后链接到心情上得到值:

result = pd.merge(person_df, klasses_df, on='klass', how='left')
print(result)

输出:

   name  feeling  klass     mood
0  John      1.5      1      Sad
1  Mary      3.6      7  Regular
2  Paul      7.2     13    Happy
3  Josh      5.7      7  Regular
4  Phil      9.9     99      NaN

(菲尔不在指定范围内,因此没有心情)

【讨论】:

    猜你喜欢
    • 2021-06-20
    • 2020-06-09
    • 2016-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多