我正在这里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)]?为什么这会在这种情况下起作用,但是当条件出现在函数中时却不起作用?
答案 0 :(得分:2)
根据您的错误消息和示例,有两件事要修复。一种是在最后的elif
语句中为运算符优先级调整括号。另一个是避免混合datetime.date
和Timestamp
对象。
修复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
,a
和b
设置为多索引并使用index-based masks to set values
Time
答案 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