使用 groupby 重新采样非定时 df
Posted
技术标签:
【中文标题】使用 groupby 重新采样非定时 df【英文标题】:Resample non timed df with a groupby 【发布时间】:2021-11-19 04:45:48 【问题描述】:我想在我的 df. 我必须使用 groupby 'name' 和 'grade' 是我尝试获取平均值的列。 'From' 和 'to' 是应用 'grade' 的垂直边界。
例子:
输入:
name from to grade
- - - -
A 0 1.5 1.0
A 1.5 3.0 1.5
A 3.0 4 1.7
B 0 3 1.6
B 3 3.7 1.9
B 3.7 5 2.0
想要的输出:
name from to grade
- - - -
A 0 1 1.0
A 1 2 1.25
A 2 3 1.5
A 3 4 1.7
B 0 1 1.6
B 1 2 1.6
B 2 3 1.6
B 3 4 1.93 (=1.9 x 0.7 + 2.0 x 0.3)
B 4 5 2.0
我正在使用 pandas 并尝试了“rolling_mean”,但效果不佳。
有什么想法吗?
编辑: 感谢 EBDS 的时间和回答,我尝试了您的代码,这里有更多详细信息:
-
根据我对这部分代码的理解:
else: grade = r1['ni']*(r1['to'] - i) + r2['ni']*(to_val - r1['to'] ) row = ['holeid': r1['holeid'],'from': i,'to': i+1,'ni': grade]
加权平均只取2个值,但1米内最多可以有10个值(但这只是我的理解,请确认)。例子: 输入:
name from to grade
- - - -
A 0.0 1.8 0.5
A 1.8 2.15 1.0
A 2.15 2.21 1.4
A 2.21 2.5 1.6
A 2.5 2.9 1.7
A 2.9 3.2 1.0
想要的输出:
name from to grade
- - - -
A 0 1.0 0.5
A 1.0 2.0 0.6 (0.8 x 0.5 + 0.2 x 1.0)
A 2.0 3.0 1.478 (0.15x1.0+0.06x1.4+0.29x1.6+0.4x1.7+0.1x1.0)
A 3.0 4.0 1.0
-
我用我的完整数据集进行了尝试,最后的长样本通常是不可分割的(如下):
EDIT2:第一个“来自”和最后一个“到”的行为 更多逻辑细节,其中 5.6 是第一个,7.9 是最后一个
输入:
name from to grade
- - - -
A 5.6 5.7 1.4
A 5.7 5.9 1.0
A 5.9 6.2 1.3
A 6.2 6.9 1.6
A 6.9 7.1 1.7
A 7.1 7.6 1.0
A 7.6 7.9 1.9
想要的输出:
name from to grade
- - - -
A 5.6 6.0 1.175 ((0.1x1.4 + 0.2x1.0 + 0.1x1.3)/0.4)
A 6.0 7.0 1.55 ((0.2x1.3 + 0.7x1.6 + 0.1x1.7)/1)
A 7.0 7.9 1.378 ((0.1x1.7 + 0.5x1.0 + 0.3x1.9)/0.9)
【问题讨论】:
嗨,你的意思是你想要weighted average
来自你的列from
和to
吗?
我在下面提出了一个可能的解决方案。如果这是您想要的,请通过检查接受答案。谢谢。
哇!这比我想象的要复杂,非常感谢!我试了一下,然后回来检查它是否有效。
我不明白你的计算。对于 (0.8 x 0.5 + 0.2 x 1.0),它没有分数 from 和 to,为什么需要计算?我也不明白你是怎么得到这个 (0.15x1.0+0.06x1.4+0.29x1.6+0.4x1.7+0.1x1.0) 计算的?你的问题很有趣。无论如何要更好地理解这个问题?
@VncJ 一些新的 /0.4, /0.9 .. 我可以说你除以 0.4 和 0.9 仅适用于非整数首先“从”和最后“到”吗?其余部分除以“1”,因此可以忽略。因为在前面给出的所有示例中,都不需要除以,我怀疑是因为它除以“1”。似乎我们有 12 小时的时差。 . 我在格林威治标准时间 +8
【参考方案1】:
这段代码非常原始。我刚开始工作。您可以在您的全套数据上进行尝试,看看是否是您想要的。然后可以修改代码。 我不知道如何以熊猫方式操纵它。我只知道编程循环/ifelse 方式。可能有人可以给你一些更优雅的东西。如果没有,你可以使用
def cal_grade(df):
df1 = pd.DataFrame(columns = ['name','from','to','grade'])
for r in range(len(df)-1):
r1 = df.iloc[r,:]
r2 = df.iloc[r+1,:]
from_val = math.ceil(r1['from'])
to_val = math.ceil(r1['to'])
if to_val == (r1['to']):
for i in range(from_val,to_val):
row = ['name': r1['name'],'from':i,'to': i+1,'grade': r1['grade']]
df1 = df1.append(row,ignore_index=True)
else:
for i in range(from_val,to_val):
if i < to_val-1:
row = ['name': r1['name'],'from': i,'to': i+1,'grade': r1['grade']]
else:
grade = r1['grade']*(r1['to'] - i) + r2['grade']*(to_val - r1['to'] )
row = ['name': r1['name'],'from': i,'to': i+1,'grade': grade]
df1 = df1.append(row,ignore_index=True)
df1 = df1.append(df.iloc[-1,:],ignore_index=True)
from_val = df1['from'].iloc[-1]
from_val1 = math.ceil(from_val)
df1.replace(from_val,from_val1,inplace=True)
return df1
df
df.groupby('name').apply(cal_grade).reset_index(drop=True)
输出
name from to grade
0 A 0.0 1.5 1.0
1 A 1.5 3.0 1.5
2 A 3.0 4.0 1.7
3 B 0.0 3.0 1.6
4 B 3.0 3.7 1.9
5 B 3.7 5.0 2.0
6 B 5.0 8.3 5.0
7 B 8.3 10.5 8.0
8 B 10.5 14.0 10.0
name from to grade
0 A 0 1.0 1.00
1 A 1 2.0 1.25
2 A 2 3.0 1.50
3 A 3 4.0 1.70
4 B 0 1.0 1.60
5 B 1 2.0 1.60
6 B 2 3.0 1.60
7 B 3 4.0 1.93
8 B 4 5.0 2.00
9 B 5 6.0 5.00
10 B 6 7.0 5.00
11 B 7 8.0 5.00
12 B 8 9.0 7.10
13 B 9 10.0 8.00
14 B 10 11.0 9.00
15 B 11 14.0 10.00
我添加了更多行来测试它。其中一些使用的变量可以被清理,但你先看看这是否是你想要的。
【讨论】:
谢谢!我有一个问题,此代码是否适用于 2 个以上的等级值?我最多可以在 1 米的深度从/深度到喜欢:name from to grade A 0 0.1 1.5 A 0.1 0.5 2.0 A 0.5 0.6 1.0 A 0.6 1.0 1.4
(抱歉格式不好,我现在甚至无法编辑,但基本上就像 1 米距离和距离之间的 10 个值)
也可以在任何地方剪切,例如:从 = 0.95,到 = 1.2
另一件事:例如,如果我有一行 from = 5 和 a to = 8,则输出应附加 3 行
1.我已经用 (5,8,3) 进行了测试,它能够“扩展”到多行。 2.你的初始问题数据显示1个单元的输出数据边界,即输出为(1,2),(2,3),(3,4),(4,5)....所以循环逻辑将数据拆分为 1 为单位(如在您的示例输出中)。您稍后在评论中提供的数据是子数据,即边界小于 1 的几个输入行。那么预期输出是多少?没有预期的输出,就无法知道是否可以完成。【参考方案2】:
代码不漂亮。它按原样工作。我希望有更好的方法来编码。这不是我可以在此编辑器上解释的单步逻辑。最好是使用 IDE 来单步执行。您必须修改您的网站。其中一些东西,比如我打印'bug',只需编辑出来。一些用于测试目的。祝你的网站好运!
from io import StringIO
from math import floor,ceil
import pandas as pd
import numpy as np
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity='all'
df = pd.read_excel('Resample non timed df with a groupby (VNC J).xlsx',sheet_name='NW04',dtype='from':np.float64,'to':np.float64) \
.rename(columns='from':'from1','to':'to1').reindex()
df['from2'] = df['from1'].apply(floor)
df['to2'] = df['to1'].apply(floor)
df
name from1 to1 grade from2 to2
0 A 0.00 3.10 7.36 0 3
1 A 3.10 3.20 5.18 3 3
2 A 3.20 3.30 7.45 3 3
3 A 3.30 3.60 8.91 3 3
4 A 3.60 4.00 7.82 3 4
5 B 4.00 5.00 8.00 4 5
6 C 5.00 6.00 8.18 5 6
7 D 6.00 7.10 7.09 6 7
8 E 7.10 7.90 6.55 7 7
9 E 7.90 8.10 9.09 7 8
10 F 8.10 12.50 1.09 8 12
11 G 12.50 15.00 7.09 12 15
12 H 15.00 15.01 3.45 15 15
13 I 15.01 15.10 13.00 15 15
df1 = df.copy(deep=True)
lst = []
accum = 0
multi_row_to = -1
multi_row_last = 0
for r in range(len(df)):
name = df.loc[r, 'name']
from1 = df.loc[r, 'from1']
to1 = df.loc[r, 'to1']
from2 = df.loc[r, 'from2']
to2 = df.loc[r, 'to2']
grade = df.loc[r, 'grade']
to_span = to2 - from2
if ((multi_row_to != -1) and (to2 != multi_row_last)):
multi_row_to = -1
if (r == len(df)-1) and (floor(from2)==floor(to2)): # last row with small from/to
multi_row_to = -1
grade_out = ((to1 - from1) * grade + accum) / (to1 - floor(to1))
accum = 0
lst.append((name, r, from1, to1, grade, from2, to1, grade_out, accum))
elif ((to1 % 1) == 0) and (to_span < 2): # no multi-rows (wrong to1 == to2)
multi_row_to = -1
grade_out = (to2 - from1) * grade + accum
accum = (to1 - to2) * grade
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
elif to1 != to2 and to_span < 2: # continue multi-rows
if multi_row_to == to2:
if to2 > from2:
multi_row_to = to2 # (next value)
grade_out = (to2 - from1) * grade + accum
accum = (to1 - to2) * grade
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
else:
grade_out = '-'
accum += (to1 - from1) * grade
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
elif multi_row_to == -1: # entered multi_row
multi_row_last = to2
if to2 > from2:
multi_row_to = to2 # (next value)
grade_out = (to2 - from1) * grade + accum
accum = (to1 - to2) * grade
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
else:
multi_row_to = to2
grade_out = '-'
accum += (to1 - from1) * grade ### Changed here bug
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
else: # switch number in multi_row
multi_row_to = to2
grade_out = (to2 - from1) * grade + accum
accum = (to1 - to2) * grade
lst.append((name, r, from1, to1, grade, from2, to2, grade_out, accum))
elif to_span >= 2: # explod grade
explode_first = True
for i in range(from2, to2+1):
if explode_first: # i == from1: # first
explode_first = False
grade_out = (i + 1 - from1) * grade + accum
accum = 0
lst.append((name, r, from1, to1, grade, i, i + 1, grade_out, accum))
elif ((i == to2) and (floor(to1) != to1)): # last decimal
grade_out = '-'
accum = (to1 - to2) * grade # changed here
lst.append((name, r, from1, to1, grade, i, to1, grade_out, accum))
# if accum != 0:
# grade_out = accum
# accum = 0
# lst.append((name, r + 1, '-', '-', '-', to2, to1, grade_out, accum))
elif ((i == (to2-1)) and (floor(to1) == to1)) : # whole number
grade_out = grade
accum = (to2 - to1) * grade
lst.append((name, r, from1, to1, grade, i, i + 1, grade_out, accum))
break # no choice, cater to last number decimal
else: # middle
if accum != 0:
grade_out = (i + 1 - from1) * grade + accum
accum = 0
lst.append((name, r, from1, to1, grade, i, i + 1, grade_out, accum))
elif (i + 1) <= to1:
grade_out = grade
accum = 0 # redundant but leave it
lst.append((name, r, from1, to1, grade, i, i + 1, grade_out, accum))
# elif (i + 1) > to1:
# grade_out = (to1 - from2) * grade
# accum = 0
# lst.append((name, r, from1, to1, grade, i, to1, grade_out, accum))
else:
print('Bug 1')
else:
print('Bug 2')
break
if accum != 0:
grade_out = accum / (to1-to2)
accum = 0
lst.append((name, r + 1, '-', '-', '-', to2, to1, grade_out, accum))
col = ['name', 'r', 'from1', 'to1', 'grade', 'from2', 'to2', 'grade_out', 'accum']
df2 = pd.DataFrame()
for i in lst:
df2 = df2.append(pd.DataFrame(i).T)
df2.columns = col
df2.reset_index(drop=True, inplace=True)
df2
name r from1 to1 grade from2 to2 grade_out accum
0 A 0 0.0 3.1 7.36 0 1 7.36 0
1 A 0 0.0 3.1 7.36 1 2 7.36 0
2 A 0 0.0 3.1 7.36 2 3 7.36 0
3 A 0 0.0 3.1 7.36 3 3.1 - 0.736
4 A 1 3.1 3.2 5.18 3 3 - 1.254
5 A 2 3.2 3.3 7.45 3 3 - 1.999
6 A 3 3.3 3.6 8.91 3 3 - 4.672
7 A 4 3.6 4.0 7.82 3 4 7.8 0.0
8 B 5 4.0 5.0 8.0 4 5 8.0 0.0
9 C 6 5.0 6.0 8.18 5 6 8.18 0.0
10 D 7 6.0 7.1 7.09 6 7 7.09 0.709
11 E 8 7.1 7.9 6.55 7 7 - 5.949
12 E 9 7.9 8.1 9.09 7 8 6.858 0.909
13 F 10 8.1 12.5 1.09 8 9 1.89 0
14 F 10 8.1 12.5 1.09 9 10 1.09 0
15 F 10 8.1 12.5 1.09 10 11 1.09 0
16 F 10 8.1 12.5 1.09 11 12 1.09 0
17 F 10 8.1 12.5 1.09 12 12.5 - 0.545
18 G 11 12.5 15.0 7.09 12 13 4.09 0
19 G 11 12.5 15.0 7.09 13 14 7.09 0
20 G 11 12.5 15.0 7.09 14 15 7.09 0.0
21 H 12 15.0 15.01 3.45 15 15 - 0.0345
22 I 13 15.01 15.1 13.0 15 15.1 12.045 0
df3 = pd.DataFrame()
newto = -1 # df2.loc[0,'to2']
for i in range(len(df2)):
# if df2.loc[i,'to2'] != newto:
if df2.loc[i,'grade_out'] != '-':
df3 = df3.append(df2.loc[i,:])
newto = df2.loc[i,'to2']
df3.drop(columns=['r','accum'],inplace=True)
df3
name from1 to1 grade from2 to2 grade_out
0 A 0.00 3.1 7.36 0.0 1.0 7.360
1 A 0.00 3.1 7.36 1.0 2.0 7.360
2 A 0.00 3.1 7.36 2.0 3.0 7.360
7 A 3.60 4.0 7.82 3.0 4.0 7.800
8 B 4.00 5.0 8.00 4.0 5.0 8.000
9 C 5.00 6.0 8.18 5.0 6.0 8.180
10 D 6.00 7.1 7.09 6.0 7.0 7.090
12 E 7.90 8.1 9.09 7.0 8.0 6.858
13 F 8.10 12.5 1.09 8.0 9.0 1.890
14 F 8.10 12.5 1.09 9.0 10.0 1.090
15 F 8.10 12.5 1.09 10.0 11.0 1.090
16 F 8.10 12.5 1.09 11.0 12.0 1.090
18 G 12.50 15.0 7.09 12.0 13.0 4.090
19 G 12.50 15.0 7.09 13.0 14.0 7.090
20 G 12.50 15.0 7.09 14.0 15.0 7.090
22 I 15.01 15.1 13.00 15.0 15.1 12.045
【讨论】:
请接受答案。谢谢。 嘿EBDS,我接受了。我找到了一种更流畅、更简单的方法来完成它,我稍后会分享。 1) 每次从整数中重新采样以追加行 2) 使用双组名和 step_from 均值以上是关于使用 groupby 重新采样非定时 df的主要内容,如果未能解决你的问题,请参考以下文章
使用特定的开始/结束日期以及 groupby 重新采样数据框
Pandas 0.18.1 groupby 和多级聚合错误重新采样