基于条件对Pandas数据框进行分组?

3

我正在遵循这里的建议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)&amp;(df ['Time']> day_1)]?为什么在这种情况下可以工作,但出现在函数中的条件却不能工作呢?


2
如果您提供数据框的样本,将会更有帮助。 - rahlf23
我的实际数据框包含公司机密信息,根据公司政策我不能分享它。但是,我编写了一个快速示例,说明了相同的错误类型。请参见编辑部分。 - Murchak
4个回答

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:更改以下三行代码:
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)

谢谢!对于我展示的简单示例,添加括号解决了问题。然而,对于我的实际示例,同样的方法不起作用,我收到了错误消息:TypeError: ("Cannot compare type 'Timestamp' with type 'date'", 'occurred at index 0')。我正在尝试理解这个错误的含义。index 0是什么意思?是数据框中的第一行吗? - Murchak
是的,索引0指的是导致错误的任何Series或DataFrame中的第一行。你的pandas版本是多少?(在Python中使用pd.__version__检查) - Peter Leimbigler
pd.version = 0.20.3(Python 3x) - Murchak
明白了。我意识到无论你使用的是哪个版本(顺便说一下,0.23已经发布了!),最快的修复方法是:将day_1 = dt.date.today()更改为day1 = pd.to_datetime('today'),并将day_2 = dt.date.today() - dt.timedelta(days = 1)更改为day_2 = day_1 - pd.DateOffset(days=1)。我已经在我的答案中进行了编辑。 - Peter Leimbigler
如果我将day_1作为参数传递给函数,例如f(row, day_1),而不是硬编码它,我该如何修改df.apply(f, axis = 1)语句?或者甚至是f(row, day_1, day_2)... - Murchak
@Murchak,这应该可以通过尽可能少的更改您的代码来实现。您可以使用类似于df.apply(row,day_1 = pd.to_datetime('2018-02-17'),day_1 = pd.to_datetime('2018-02-16'))的调用方法。或者您是指以编程方式选择day_1day_2而不是手动指定它们?这可能最好作为一个新问题提出。 - Peter Leimbigler

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)

1
如果您不需要使用自定义函数,则可以使用多个掩码(类似于此SO帖子
对于时间列,我使用了这段代码。可能是因为您试图比较没有所需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

另一种方法是将abTime设置为多索引,并使用基于索引的掩码来设置值

mydf.set_index(['a','b','Time'], inplace=True)

# Get Index level values
a = mydf.index.get_level_values('a')
b = mydf.index.get_level_values('b')
t = mydf.index.get_level_values('Time')

# Apply index-based masks
mydf['GRP'] = 'same'
mydf.loc[(a < 5) & (b < 0.5) & (t < day_2), 'GRP'] = 'less'
mydf.loc[(a < 5) & (b > 0.5) & (t > day_1), 'GRP'] = 'more'
mydf.reset_index(drop=False, inplace=True)
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”的df(应用函数)非常好,现在加上如上所述的括号后,仍然会出现错误的真实示例。 - Murchak
我已经编辑了我的答案,包括你在原帖中提到的“Time”列。请注意:我不得不更改你对“date_1”和“date_2”的定义——我为这两个变量做了一些猜测。 - edesz

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接