Pandas 数据框分组

4

我有一个包含深度列的数据框,深度间隔为0.1米。

import pandas as pd


df1 = pd.DataFrame({'depth': [1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1 ],
            '350': [7.898167, 6.912074, 6.049002, 5.000357, 4.072320, 3.070662, 2.560458, 2.218879, 1.892131, 1.588389, 1.573693],
            '351': [8.094912, 7.090584, 6.221289, 5.154516, 4.211746, 3.217615, 2.670147, 2.305846, 1.952723, 1.641423, 1.622722],
            '352': [8.291657, 7.269095, 6.393576, 5.308674, 4.351173, 3.364569, 2.779837, 2.392813, 2.013316, 1.694456, 1.671752],
            '353': [8.421007, 7.374317, 6.496641, 5.403691, 4.439815, 3.412494, 2.840625, 2.443868, 2.069017, 1.748445, 1.718081 ],
            '354': [8.535562, 7.463452, 6.584512, 5.485725, 4.517310, 3.438680, 2.890678, 2.487039, 2.123644, 1.802643, 1.763818 ],
            '355': [8.650118, 7.552586, 6.672383, 4.517310, 4.594806, 3.464867, 2.940732, 2.530211, 2.178271, 1.856841, 1.809555 ]},
            index=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
             )

我的问题是:如何将数据划分成0.5米深度频率的新数据框?
或者说,我应该如何对df1中的列值进行平均(每0.1米有一个数据),以便得出dz=0.5米的区间?
重点是要获得相同的数据框结构,相同的列(350-355),但是行应该按照一定的dz间隔(行数)进行平均/分组,假设为0.5米。
因此,在这种情况下,我的新数据框将只有两行,深度值为1.35和1.85米,保持每个列在df1中的值。第一行将平均值为1.1-1.6米的间隔,第二行为1.6-2.1米的间隔。

2
请进一步解释。 - Rahul Agarwal
@RahulAgarwal,感谢您指出。我希望现在更清晰了。 - PEBKAC
抱歉,但我个人仍然不明白你的意思。 - Rahul Agarwal
将非时间序列数据重新采样 - JohnE
@JohnE:不是真的。关键是要获得相同的df结构,相同的列(350-355),但是行应该按照一定的dz间隔(行数)进行平均/分组,比如说0.5米。 - PEBKAC
@RahulAgarwal,再清晰不过了,请查看最新的编辑,如果您感兴趣。 - PEBKAC
3个回答

5

使用 df.groupbypd.cut 的组合。

import pandas as pd
import numpy as np

# Specifiy your desired dz step size
step = 0.5
dz = np.arange(1,3,step)

# rebin dataframe
df2 = df1.groupby(pd.cut(df1.depth, dz, labels=False), as_index=False).mean()

# refill 'depth' column
df2.depth = dz[:-1]

提供

depth   350     351     352     353     354     355
0   1.0     5.986384    6.154609    6.322835    6.427094    6.517312    6.397441
1   1.5     2.266104    2.357551    2.448998    2.502890    2.548537    2.594184
2   2.0     1.573693    1.622722    1.671752    1.718081    1.763818    1.809555

每行都包含在1 < x <= 1.51.5 < x <= 2等范围内35x列的平均值。

您可以通过选择所需的step变量值轻松更改重新分组。


太棒了!非常感谢!只有一个问题:是否可以将dz链接到df1深度列?假设我知道步骤,但深度范围从一个数据框架变化到另一个数据框架怎么办? - PEBKAC
2
@PEBKAC:那不就是 dz = np.arange(df1.depth.min(), df1.depth.max(), step) 吗? - Daren Thomas
@DarenThomas,是的,你说得完全正确。这可能是最优雅的解决方案!非常感谢大家! - PEBKAC
1
@PEBKAC 这个答案很好(比我的好;-),但我认为可以进一步简化为以下两行,但我会把编辑留给你或gehbiszumeis:(1)df1.depth = pd.cut(df1.depth, pd.interval_range(start=1.0,end=2.5,freq=0.5))(2)df1.groupby('depth').mean() - JohnE
1
谢谢,很高兴能帮忙!不要重命名“depth”,而是创建一个新变量“depth_interval”,并在其上进行分组。然后您应该保留深度列,它将像其他列一样被平均。 - JohnE
显示剩余4条评论

2

您只需要定义一个分组器,然后进行标准的分组操作。一种方法是将值翻倍,然后使用整数截断。请注意,我还减去了一个微小的量(sub(0.001)),这样就可以与@gehbiszumeis答案中的箱边界匹配--但这只是任意的箱边界决策,可以省略。

df1['grp'] = df1.depth.sub(0.001).mul(2).astype(int)
df1.groupby('grp').mean()

结果:

    depth       350       351       352       353       354       355
grp                                                                   
2      1.3  5.986384  6.154609  6.322835  6.427094  6.517312  6.397441
3      1.8  2.266104  2.357551  2.448998  2.502890  2.548537  2.594184
4      2.1  1.573693  1.622722  1.671752  1.718081  1.763818  1.809555

或者您可以像 @gehbiszumeis 一样使用 pd.bin 方法,这种方法更加优美。以下是该方法的一个变体,可以生成区间索引:

df1['depth_range'] = pd.cut( df1.depth, pd.interval_range(start=1.0,end=2.5,freq=0.5) )
df1.groupby('depth_range').mean()

             depth       350       351       352       353       354       355
depth_range                                                                   
(1.0, 1.5]     1.3  5.986384  6.154609  6.322835  6.427094  6.517312  6.397441
(1.5, 2.0]     1.8  2.266104  2.357551  2.448998  2.502890  2.548537  2.594184
(2.0, 2.5]     2.1  1.573693  1.622722  1.671752  1.718081  1.763818  1.809555

谢谢,问题是我想要“缩小”数据框并平均所有的n行,因此在这种情况下,首先平均深度层从1.1到1.6米,得到每列的平均输出,然后移动到下一个形成0.5个箱子的行块。 - PEBKAC
看起来很酷,谢谢。但是,如果我们在1.1和2.1之间有一个范围,我不确定为什么你会得到3行和这样的值?第一层不应该包括1.1-1.6区间,第二层包括接下来的5行,以此类推吗?所以在这种情况下,平均值不应该是1.35和1.85吗?mul(2)在这种情况下的作用是什么? - PEBKAC
1
@PEBKAC 没问题,顺便说一下,我添加了 sub(0.001) 只是为了让两个答案的结果基本相同,如果箱边对齐的话。 - JohnE

1
这个怎么样:

What about something like this:

In [33]: g = df1.groupby(by=lambda i: 1.35 if i < 1.7 else 1.85)

In [34]: g.mean()
Out[34]:
           350       351       352       353       354       355  depth
1.35  7.898167  8.094912  8.291657  8.421007  8.535562  8.650118   1.10
1.85  3.493796  3.608861  3.723926  3.794699  3.855750  3.811756   1.65

请注意,您仍需要更新“深度”列。此外,groupbyby函数可能可以更加“智能” - 根据数据框自动确定要使用的深度...


是的,我正在尝试弄清楚如何编写一个条件语句,以便可以定义用于分组的dz值?就像df1.groupby(df.index / n).mean()这样的语句。 - PEBKAC

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