熊猫小组:如何在每个小组的多列中找到N个最大值?

时间:2018-08-09 12:13:25

标签: pandas pandas-groupby

我记录设备并每15分钟读取3个值(W1W2W3)。它们可以重复。

我需要每小时查找在该时间间隔内已读取的12个值中最大的3个值。我不知道何时发生,只有它们的价值。

目前,我的算法还远远不够高效和快速:

  • 遍历每个组:
    1. 将W1,W2和W3转换为列表
    2. 将3个列表合并在一起
    3. 排序组合列表
    4. 选择最大的元素。

我想删除循环,并使用本机pandas / numpy方法。可能吗?

编辑:在本文结尾处提出了一个可行的解决方案

这是代码:

from datetime import *
import pandas as pd
import numpy as np

df = pd.DataFrame()

date_ref = datetime(2017,12,8,0,0,0)
days = pd.date_range(date_ref, date_ref + timedelta(0.11), freq='15min')

np.random.seed(seed=1111)
data1 = np.random.randint(1, high=100, size=len(days))
data2 = data1 - np.random.randint(3, high=13, size=len(days))
data3 = data2 - np.random.randint(3, high=13, size=len(days))

df = pd.DataFrame({'TIME': days, 'W1': data1, 'W2': data2, 'W3': data3 })
df = df.set_index('TIME')

print("Original data")
print("-------------")
print(df)
print("**********************************************")

# groupby
grouped = df.groupby(pd.Grouper(freq='1H'))
print("Grouped data")
print("------------")
print list(grouped)
print("**********************************************")

print("3 largest values")
print("----------------")
for dtime, group in grouped:
    w  = list(group["W1"])
    w2 = list(group["W2"])
    w3 = list(group["W3"])
    w.extend(w2)
    w.extend(w3)
    w = sorted(w)

    max1 = w[-1]
    max2 = w[-2]
    max3 = w[-3]

    print(dtime, max1, max2, max3)

返回:

Original data
-------------
                     W1  W2  W3
TIME                           
2017-12-08 00:00:00  78  67  57
2017-12-08 00:15:00  73  64  59
2017-12-08 00:30:00  55  50  47
2017-12-08 00:45:00  67  58  51
2017-12-08 01:00:00  62  51  40
2017-12-08 01:15:00  52  40  32
2017-12-08 01:30:00  70  64  56
2017-12-08 01:45:00  74  67  63
2017-12-08 02:00:00  72  61  56
2017-12-08 02:15:00  70  58  55
2017-12-08 02:30:00  61  49  39
**********************************************
Grouped data
------------
[(Timestamp('2017-12-08 00:00:00', freq='H'),                      W1  W2  W3
TIME                           
2017-12-08 00:00:00  78  67  57
2017-12-08 00:15:00  73  64  59
2017-12-08 00:30:00  55  50  47
2017-12-08 00:45:00  67  58  51), (Timestamp('2017-12-08 01:00:00', freq='H'),                      W1  W2  W3
TIME                           
2017-12-08 01:00:00  62  51  40
2017-12-08 01:15:00  52  40  32
2017-12-08 01:30:00  70  64  56
2017-12-08 01:45:00  74  67  63), (Timestamp('2017-12-08 02:00:00', freq='H'),                      W1  W2  W3
TIME                           
2017-12-08 02:00:00  72  61  56
2017-12-08 02:15:00  70  58  55
2017-12-08 02:30:00  61  49  39)]
**********************************************
3 largest values
----------------
(Timestamp('2017-12-08 00:00:00', freq='H'), 78, 73, 67)
(Timestamp('2017-12-08 01:00:00', freq='H'), 74, 70, 67)
(Timestamp('2017-12-08 02:00:00', freq='H'), 72, 70, 61)

解决方案

在我的代码中实现该解决方案时遇到了一些麻烦,因此在此保留后代的最终版本。也许对某人有用。

即使@jezrael的解决方案可以在我的演示中使用,也不能在我的最终版本中使用。它抱怨不可否认的时间戳。调试熊猫组非常困难,因此我使用了@GeorgeLPerkins。 (对我来说)更容易理解。

最大的问题是grouped.apply()返回一系列列表。

使用str从每个列表中提取每个元素:顾名思义,我认为它只与字符串有关,而没有考虑...

现在,避免了每个直接循环,结果gdf是一个数据框,可以通过一次写入操作将其保存到数据库中。

我是熊猫的新手,bie,我认为这可以进行高度优化。

from datetime import *
import pandas as pd
import numpy as np

df = pd.DataFrame()

date_ref = datetime(2017,12,8,0,0,0)
days = pd.date_range(date_ref, date_ref + timedelta(0.11), freq='15min')

np.random.seed(seed=1111)
data1 = np.random.randint(50, high=80, size=len(days))
data2 = data1 - np.random.randint(3, high=13, size=len(days))
data3 = data2 - np.random.randint(3, high=13, size=len(days))

df = pd.DataFrame({'TIME': days, 'W1': data1, 'W2': data2, 'W3': data3 })
df = df.set_index('TIME')

#print("Original data")
#print("-------------")
#print(df)
#print("**********************************************")

# groupby
grouped = df.groupby(pd.Grouper(freq='1H'))
print("Grouped data")
print("------------")
print list(grouped)
print("**********************************************")

print("3 largest values")
print("----------------")

def operation(x):
    combinedcoltop3 = []
    combinedcoltop3.extend(list(x.nlargest(3, "W1")["W1"])) # reads the 3 largest W1 and return W1 only
    combinedcoltop3.extend(list(x.nlargest(3, "W2")["W2"]))
    combinedcoltop3.extend(list(x.nlargest(3, "W3")["W3"]))
    combinedcoltop3.sort(reverse=True)
    return combinedcoltop3[:3]  # returns a list!

df1 = grouped.apply(operation) 

gdf = pd.DataFrame()
gdf["W1"] = df1.str[0]  # reads each element of the list for each row of df1
gdf["W2"] = df1.str[1]
gdf["W3"] = df1.str[2]

print(gdf)

# now gdf can be saved with a single write into the database

2 个答案:

答案 0 :(得分:1)

您可以先通过numpy.ravelsort it in descending order将所有值展平到1d数组,然后通过建立索引返回顶部3个值:

df1 = df.groupby(pd.Grouper(freq='1H')).apply(lambda x: -np.sort(-np.ravel(x))[:3])
print (df1)
TIME
2017-12-08 00:00:00    [78, 73, 67]
2017-12-08 01:00:00    [74, 70, 67]
2017-12-08 02:00:00    [72, 70, 61]
Freq: H, dtype: object

如果需要列:

i = ['top1','top2','top3']
df1 = (df.groupby(pd.Grouper(freq='1H'))
        .apply(lambda x: pd.Series(-np.sort(-np.ravel(x))[:3], index=i)))
print (df1)
                     top1  top2  top3
TIME                                 
2017-12-08 00:00:00    78    73    67
2017-12-08 01:00:00    74    70    67
2017-12-08 02:00:00    72    70    61

答案 1 :(得分:1)

我发现更简单的是:

combinedcoltop3 = []
for col in df.columns:
    combinedcoltop3.extend(list(df[col].nlargest(3)))

combinedcoltop3.sort(reverse=True)
top3 = combinedcoltop3[:3]
相关问题