当达到最大值时,计算Pandas列的累积和,并计算相邻行的平均值

3

我是一名生物学学生,对Python还比较新,希望有人能够帮助我解决一个问题。

通过一些后续代码,我创建了一个类似下面示例的Pandas数据框:

Distance.     No. of values        Mean rSquared
    1                   500                  0.6
    2                    80                  0.3
    3                    40                  0.4
    4                    30                  0.2
    5                    50                  0.2
    6                    30                  0.1

我可以提供我之前创建这个数据框的代码,但我认为它不是特别相关。
我需要对“数值数量”列进行求和,直到达到值≥100;然后结合相邻列的行数据,按距离和平均r2值的加权平均值进行计算,如下例所示。
Mean Distance.             No. Of values             Mean rSquared
1                          500                       0.6
(80*2+40*3)/120            (80+40) = 120             (80*0.3+40*0.4)/120
(30*4+50*5+30*6)/110       (30+50+30) = 110          (30*0.2+50*0.2+30*0.1)/110

etc...

我知道pandas有一个.cumsum函数,我可能可以将它与一个for循环和一个if语句结合起来使用,该语句检查上限并在大于或等于上限时将总和重置为0。然而,我不知道如何对相邻的列进行平均。

任何帮助都将不胜感激!


使用这种阈值,最好编写一个for循环。 - Quang Hoang
你介意详细说明一下那个for循环需要放入什么吗? - James
3个回答

2
您可以使用此代码片段来解决您的问题。
# First, compute some weighted values
df.loc[:, "weighted_distance"] = df["Distance"] * df["No. of values"]
df.loc[:, "weighted_mean_rSquared"] = df["Mean rSquared"] * df["No. of values"]


min_threshold = 100
indexes = []
temp_sum = 0

# placeholder for final result
final_df = pd.DataFrame()
columns = ["Distance", "No. of values", "Mean rSquared"]

# reseting index to make the 'df' usable in following output
df = df.reset_index(drop=True)

# main loop to check and compute the desired output
for index, _ in df.iterrows():
    temp_sum += df.iloc[index]["No. of values"]
    indexes.append(index)

    # if the sum exceeds 'min_threshold' then do some computation
    if temp_sum >= min_threshold:
        temp_distance = df.iloc[indexes]["weighted_distance"].sum() / temp_sum
        temp_mean_rSquared = df.iloc[indexes]["weighted_mean_rSquared"].sum() / temp_sum
    
        # create temporary dataframe and concatenate with the 'final_df'
        temp_df = pd.DataFrame([[temp_distance, temp_sum, temp_mean_rSquared]], columns=columns)
        final_df = pd.concat([final_df, temp_df])
    
        # reset the variables
        temp_sum = 0
        indexes = []

非常感谢您的回复,我没想到最终会得到答案,所以我在 Python 中摸索了一番,并得出了类似的解决方案,因此我将答案给予您,再次感谢!编辑:由于不知道回车键会提交评论,所以我一开始没有完成评论(呃)。 - James

1
Numpy有一个函数numpy.frompyfunc,您可以使用它来根据阈值获取累积值。
以下是实现方法。然后,您可以找出当值超过阈值时的索引。使用该索引计算原始数据框中的值的平均距离平均rSquared
我还利用了@sujanay的想法先计算加权值。
c = ['Distance','No. of values','Mean rSquared']
d = [[1,500,0.6], [2,80,0.3], [3,40,0.4],
     [4,30,0.2], [5,50,0.2], [6,30,0.1]]

import pandas as pd
import numpy as np

df = pd.DataFrame(d,columns=c)

#calculate the weighted distance and weighted mean squares first
df.loc[:, "w_distance"] = df["Distance"] * df["No. of values"]
df.loc[:, "w_mean_rSqrd"] = df["Mean rSquared"] * df["No. of values"]

#use numpy.frompyfunc to setup the threshold condition

sumvals = np.frompyfunc(lambda a,b: a+b if a <= 100 else b,2,1)

#assign value to cumvals based on threshold
df['cumvals'] = sumvals.accumulate(df['No. of values'], dtype=np.object)

#find out all records that have >= 100 as cumulative values
idx = df.index[df['cumvals'] >= 100].tolist()

#if last row not in idx, then add it to the list
if (len(df)-1) not in idx: idx += [len(df)-1]

#iterate thru the idx for each set and calculate Mean Distance and Mean rSquared
i = 0
for j in idx:
    df.loc[j,'Mean Distance'] = (df.iloc[i:j+1]["w_distance"].sum() / df.loc[j,'cumvals']).round(2)
    df.loc[j,'New Mean rSquared'] = (df.iloc[i:j+1]["w_mean_rSqrd"].sum() / df.loc[j,'cumvals']).round(2)
    i = j+1

print (df)

这段话的翻译是:“这将产生以下输出:”
   Distance  No. of values  ...  Mean Distance  New Mean rSquared
0         1            500  ...           1.00               0.60
1         2             80  ...            NaN                NaN
2         3             40  ...           2.33               0.33
3         4             30  ...            NaN                NaN
4         5             50  ...            NaN                NaN
5         6             30  ...           5.00               0.17

如果您想提取仅为非 NaN 的记录,则可以执行以下操作:

final_df = df[df['Mean Distance'].notnull()]

这将导致:
   Distance  No. of values  ...  Mean Distance  New Mean rSquared
0         1            500  ...           1.00               0.60
2         3             40  ...           2.33               0.33
5         6             30  ...           5.00               0.17

我查阅了BEN_YO对numpy.frompyfunc的实现。原始的SO帖子可以在这里找到。如果累加和大于某个值,重新开始cumsum并获取索引

1
如果你先确定了分组,pandas的groupby功能会为你做剩下的工作。使用循环来获取分组是合适的(除非有人有巧妙的一行代码):
>>> groups = []
>>> group = 0
>>> cumsum = 0
>>> for n in df["No. of values"]:
...     if cumsum >= 100:
...         cumsum = 0
...         group = group + 1
...     cumsum = cumsum + n
...     groups.append(group)
>>>
>>> groups
[0, 1, 1, 2, 2, 2]

在进行分组操作之前,您需要使用值的数量信息来获取加权值:
df[["Distance.", "Mean rSquared"]] = df[["Distance.", "Mean rSquared"]].multiply(df["No. of values"], axis=0)

现在像这样得到总和:

>>> sums = df.groupby(groups)["No. of values"].sum()
>>> sums
0    500
1    120
2    110
Name: No. of values, dtype: int64

最后,加权组平均值如下:
>>> df[["Distance.", "Mean rSquared"]].groupby(groups).sum().div(sums, axis=0)
   Distance.  Mean rSquared
0   1.000000       0.600000
1   2.333333       0.333333
2   5.000000       0.172727

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