【问题标题】:How to normalize values in a dataframe column in different ranges如何标准化数据框列中不同范围内的值
【发布时间】:2016-04-25 03:45:27
【问题描述】:

我有一个这样的数据框:

    T  data
0   0    10
1   1    20
2   2    30
3   3    40
4   4    50
5   0     5
6   1    13
7   2    21
8   0     3
9   1     7
10  2    11
11  3    15
12  4    19

T 中的值是范围从 0 到某个值的序列,其中序列之间的最大值可能不同。 通常,data 中的值不等间距,现在仅用于演示目的。

我想要实现的是添加名为dataDiv 的第三列,其中某个序列的data 中的每个值除以属于相应序列的T = 0 中的值。就我而言,我有 3 个序列,对于第一个序列,我想将每个值除以 10,在第二个序列中,每个值应该除以 5,第三个序列中的每个值除以 3。 所以预期的结果是这样的:

    T  data   dataDiv
0   0    10  1.000000
1   1    20  2.000000
2   2    30  3.000000
3   3    40  4.000000
4   4    50  5.000000
5   0     5  1.000000
6   1    13  2.600000
7   2    21  4.200000
8   0     3  1.000000
9   1     7  2.333333
10  2    11  3.666667
11  3    15  5.000000
12  4    19  6.333333

我目前实现的方式如下: 我首先确定T = 0 所在的索引。然后我循环遍历这些索引并将data 中的数据除以相应序列的T=0 处的值,从而得到所需的输出(如上所示)。代码如下:

import pandas as pd
df = pd.DataFrame({'T': range(5) + range(3) + range(5),
                   'data': range(10, 60, 10) + range(5, 25, 8) + range(3, 21, 4)})

# get indices where T = 0
idZE = df[df['T'] == 0].index.tolist()

# last index of dataframe
idZE.append(max(df.index)+1)

# add the column with normalzed values
df['dataDiv'] = df['data']

# loop through indices where T = 0 and normalize values
for ix, indi in enumerate(idZE[:-1]):

    df['dataDiv'].iloc[indi:idZE[ix + 1]] = df['data'].iloc[indi:idZE[ix + 1]] / df['data'].iloc[indi]

我的问题是:有没有比这更聪明的解决方案来避免循环?

【问题讨论】:

    标签: python performance pandas dataframe normalization


    【解决方案1】:

    如果有利于矢量化计算,以下方法可以避免循环,并且应该执行得更快。基本思想是在“T”列中标记整数运行,找到每个组中的第一个值,然后将“数据”中的值除以适当的第一个值。

    df['grp'] = (df['T'] == 0).cumsum()           # label consecutive runs of integers
    x = df.groupby('grp')['data'].first()         # first value in each group
    df['dataDiv'] = df['data'] / df['grp'].map(x) # divide 
    

    这为 DataFrame 提供了所需的列:

        T  data  grp   dataDiv
    0   0    10    1  1.000000
    1   1    20    1  2.000000
    2   2    30    1  3.000000
    3   3    40    1  4.000000
    4   4    50    1  5.000000
    5   0     5    2  1.000000
    6   1    13    2  2.600000
    7   2    21    2  4.200000
    8   0     3    3  1.000000
    9   1     7    3  2.333333
    10  2    11    3  3.666667
    11  3    15    3  5.000000
    12  4    19    3  6.333333
    

    (如果愿意,您可以删除“grp”列:df.drop('grp', axis=1)。)

    正如@DSM 在下面指出的,使用groupby.transform 可以将三行代码合并为一行:

    df['dataDiv'] = df['data'] / df.groupby((df['T'] == 0).cumsum())['data'].transform('first')
    

    【讨论】:

    • 看起来不错,工作正常,谢谢!我暂时赞成它,以后可能会接受它,具体取决于其他答案的质量。
    • 不值得单独回答,但您也可以使用.transform("first"),然后除以x 而不是df["grp"].map(x)。数量相同。
    • @DSM:感谢您的评论。我仍在探索熊猫的力量,所以我总是很高兴能学到新东西。
    • @DSM:感谢您的建议 - transform 在某种程度上简化了代码。我已将其编辑到我的答案中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 2019-06-14
    • 2016-01-07
    • 2020-07-19
    • 2018-09-24
    • 1970-01-01
    相关资源
    最近更新 更多