根据条件对熊猫数据框进行分组吗?

时间:2018-10-23 23:41:48

标签: python pandas dataframe

我正在这里pandas create new column based on values from other columns的建议,但仍然遇到错误。基本上,我的Pandas数据框有很多列,我想根据一个新的分类列对数据框进行分组,该列的值取决于两个现有列(AMP,Time)。

df
df['Time'] = pd.to_datetime(df['Time']) 
#making sure Time column read from the csv file is time object

import datetime as dt
day_1 = dt.date.today()
day_2 = dt.date.today() - dt.timedelta(days = 1)

def f(row):

    if (row['AMP'] > 100) & (row['Time'] > day_1):
        val = 'new_positives'

    elif (row['AMP'] > 100) & (day_2 <= row['Time'] <= day_1):
        val = 'rec_positives'

    elif (row['AMP'] > 100 & row['Time'] < day_2):
        val = 'old_positives'

    else:
        val = 'old_negatives'

    return val

df['GRP'] = df.apply(f, axis=1) #this gives the following error:
TypeError: ("Cannot compare type 'Timestamp' with type 'date'", 'occurred at index 0')

df[(df['AMP'] > 100) & (df['Time'] > day_1)]  #this works fine

df[(df['AMP'] > 100) & (day_2 <= df['Time'] <= day_1)]  #this works fine

df[(df['AMP'] > 100) & (df['Time'] < day_2)]  #this works fine


#df = df.groupby('GRP')  

我能够根据上面指定的条件选择合适的子数据帧,但是当我在每行上应用上面的函数时,我得到了错误。根据列出的条件对数据框进行分组的正确方法是什么?

编辑:

很遗憾,我无法提供我的数据框示例。但是,以下是一个简单的数据框,它给出了相同类型的错误:

import numpy as np
import pandas as pd
mydf = pd.DataFrame({'a':np.arange(10),
   'b':np.random.rand(10)})

def f1(row):
    if row['a'] < 5 & row['b'] < 0.5:
        value = 'less'
    elif row['a'] < 5 & row['b'] > 0.5:
        value = 'more'
    else:
        value = 'same'
    return value

mydf['GRP'] = mydf.apply(f1, axis=1)

ypeError: ("unsupported operand type(s) for &: 'int' and 'float'", 'occurred at index 0')

编辑2: 如下所建议的,将比较运算符括在括号内可以完成示例。这个问题解决了。

但是,在我的真实示例中,我仍然遇到相同的错误。顺便说一句,如果我将“ AMP”列与表中的另一列一起使用,那么一切正常,我可以通过将f应用于每行来创建df ['GRP']。这表明问题与使用df ['Time']有关。但是,为什么我可以选择df [(df ['AMP']> 100)和(df ['Time']> day_1)]?为什么这会在这种情况下起作用,但是当条件出现在函数中时却不起作用?

4 个答案:

答案 0 :(得分:2)

根据您的错误消息和示例,有两件事要修复。一种是在最后的elif语句中为运算符优先级调整括号。另一个是避免混合datetime.dateTimestamp对象。

修复1:更改此内容:

elif (row['AMP'] > 100 & row['Time'] < day_2):

对此:

elif (row['AMP'] > 100) & (row['Time'] < day_2):

这两行是不同的,因为按位&运算符优先于<>比较运算符,因此python尝试求值100 & row['Time']。以下是Python运算符优先级的完整列表:https://docs.python.org/3/reference/expressions.html#operator-precedence

修复2:更改以下3行:

import datetime as dt
day_1 = dt.date.today()
day_2 = dt.date.today() - dt.timedelta(days = 1)

这两行:

day1 = pd.to_datetime('today')
day_2 = day_1 - pd.DateOffset(days=1)

答案 1 :(得分:1)

if语句中需要添加一些括号:

import numpy as np
import pandas as pd

mydf = pd.DataFrame({'a':np.arange(10),
   'b':np.random.rand(10)})

def f1(row):
    if (row['a'] < 5) & (row['b'] < 0.5):
        value = 'less'
    elif (row['a'] < 5) & (row['b'] > 0.5):
        value = 'more'
    else:
        value = 'same'
    return value

mydf['GRP'] = mydf.apply(f1, axis=1)

答案 2 :(得分:1)

如果您不需要使用自定义功能,则可以使用多个掩码(somewhat similar to this SO post

对于Time column,我使用了这段代码。可能是您正在尝试比较没有必需的Time的{​​{1}}列值(这是我的猜测)

dtype

这是原始数据

import datetime as dt
mydf['Time'] = pd.date_range(start='10/14/2018', end=dt.date.today())
day_1 = pd.to_datetime(dt.date.today())
day_2 = day_1 - pd.DateOffset(days = 1)

一种方法涉及对列使用掩码

mydf

   a         b       Time
0  0  0.550149 2018-10-14
1  1  0.889209 2018-10-15
2  2  0.845740 2018-10-16
3  3  0.340310 2018-10-17
4  4  0.613575 2018-10-18
5  5  0.229802 2018-10-19
6  6  0.013724 2018-10-20
7  7  0.810413 2018-10-21
8  8  0.897373 2018-10-22
9  9  0.175050 2018-10-23

另一种方法是将# Append new column mydf['GRP'] = 'same' # Use masks to change values in new column mydf.loc[(mydf['a'] < 5) & (mydf['b'] < 0.5) & (mydf['Time'] < day_2), 'GRP'] = 'less' mydf.loc[(mydf['a'] < 5) & (mydf['b'] > 0.5) & (mydf['Time'] > day_1), 'GRP'] = 'more' mydf a b Time GRP 0 0 0.550149 2018-10-14 same 1 1 0.889209 2018-10-15 same 2 2 0.845740 2018-10-16 same 3 3 0.340310 2018-10-17 less 4 4 0.613575 2018-10-18 same 5 5 0.229802 2018-10-19 same 6 6 0.013724 2018-10-20 same 7 7 0.810413 2018-10-21 same 8 8 0.897373 2018-10-22 same 9 9 0.175050 2018-10-23 same ab设置为多索引并使用index-based masks to set values

Time

来源为filter by datetimecreate a range of dates

答案 3 :(得分:0)

您在这里有一个出色的示例,它非常有用,可以在groupby之后应用过滤器。这是不使用遮罩的一种方式。

def get_letter_type(letter):
   if letter.lower() in 'aeiou':
       return 'vowel'
   else:
       return 'consonant'


In [6]: grouped = df.groupby(get_letter_type, axis=1)

https://pandas.pydata.org/pandas-docs/version/0.22/groupby.html