加速循环

ale*_*nov 8 python pandas

我有一个可能来自数据透视表的数据框,所以它看起来很混乱。它不仅仅是一个 XY 2 维数据,而是一个 4 或 5。

我想要做的是,如果满足某些条件,则添加一个新列并从第 3 行(第 0、1、2 行是字符串)开始计算同比变化。

并且为了在计算时避免零除法错误(如果直接进行数学运算,我会得到错误),我必须在循环中添加循环(if else首先输入 0 的函数)。通过使用time()函数,我可以看出程序每次运行都需要将近 10 分钟。

我想知道是否有任何更聪明的方法来编程它。感谢您的善意建议!

for i in range(1, 187):
   k = i * 1000
   if df.iloc[1, i] == df.iloc[1, i - 1] and df.iloc[1, i] != df.iloc[1, i+ 1]:
        df[k] = 0
        a = df.columns.get_loc[k]
        df.iloc[0, a] = df.iloc[0, i]
        df.iloc[1, a] = df.iloc[1, i]
        df.iloc[2, a] = 'YoY%'
        
        for m in range(3, 13259):
            if df.iloc[m, i - 1] == 0:
                 df.iloc[m, a] = 0
            else:
                 df.iloc[m, a] = (df.iloc[m, i] - df.iloc[m, i - 1]) / df.iloc[m, i - 1]
df = df.sort_values(by=[0, 1, 2], axis=1)
Run Code Online (Sandbox Code Playgroud)

排序前的数据框片段如下所示:

0      cash    cash    cash     cash     trade   trade    cash    cash    trade
1      54100   54100   54103    54103    52209   52209    54100   54103   52209
2      02-21   03-21   02-21    03-21    02-21   03-21    YoY%    YoY%    YoY%
3      0       0       1500     1500     -28.1   -2476.8  0       0       87.14 
4      0       500     16500    16500    25      35       0       0       0.4
5      0       0       300      600      3.1     420      0       1       134.5
       ..      ..      ..       ..       ..      ..       ..      ..      ..
13258  1973.7  4484.9  18300    18600    0       750      1.3     0.02    0
Run Code Online (Sandbox Code Playgroud)

jab*_*lcu 3

如果您以易于复制和使用的格式提供数据示例,那么回答您的问题会更容易。我会尽力帮助:

假设您有以下数据:

records = [
    ['cash','cash','cash','cash','trade','trade'],
    [54100,54100,54103,54103,52209,52209],
    ['02-21','03-21','02-21','03-21','02-21','03-21'],
    [0,0,1500,1500,-28.1,-2476.8],
    [0,500,16500,16500,25,35],
    [0,0,300,600,3.1,420],
    [1973.7,4484.9,18300,18600,0,750],
]
index = [0,1,2,3,4,5,13259]
df = pd.DataFrame.from_records(records, index=index)
Run Code Online (Sandbox Code Playgroud)

它产生以下数据帧:

            0       1      2      3      4       5
0        cash    cash   cash   cash  trade   trade
1       54100   54100  54103  54103  52209   52209
2       02-21   03-21  02-21  03-21  02-21   03-21
3           0       0   1500   1500  -28.1 -2476.8
4           0     500  16500  16500     25      35
5           0       0    300    600    3.1     420
13259  1973.7  4484.9  18300  18600      0     750
Run Code Online (Sandbox Code Playgroud)

看起来数据的前三行实际上是表标题。我们可以将它们变成多索引以充分利用 pandas 的功能:

# Set columns index from first rows
df.columns = [df.iloc[i] for i in range(3)]
# Delete first rows used for column names
df = df.iloc[3:]
Run Code Online (Sandbox Code Playgroud)

这会生成以下数据框(请注意,前三行现在是数据框的列):

0        cash                       trade
1       54100          54103        52209
2       02-21   03-21  02-21  03-21 02-21   03-21
3           0       0   1500   1500 -28.1 -2476.8
4           0     500  16500  16500    25      35
5           0       0    300    600   3.1     420
13259  1973.7  4484.9  18300  18600     0     750
Run Code Online (Sandbox Code Playgroud)

现在您可以使用 pandas 沿列进行分组。首先,我们需要一个函数来对每对列进行分组时应用它们。假设我们定义以下函数来计算两个月之间的增量:

def calculate_incr(df):
    ''' Calculates the increment between a dataframe's first two colums.
    Ignores division by zero errors.
    '''
    mask = df.iloc[:,0] != 0  # prevents divide by zero error
    return df.loc[mask].iloc[:,1] / df.loc[mask].iloc[:,0] - 1
Run Code Online (Sandbox Code Playgroud)

我们最终可以对数据进行分组并计算增量。这有点棘手,因为 pandas 似乎不喜欢在多个列级别上进行分组,因此我们将首先组合列标签,然后在后续步骤中再次拆分它们以与原始数据组合:

In []: df.groupby(by=lambda l: '_'.join(map(str, l[:2])), axis=1).agg(calculate_incr)
Out[]:
       cash_54100  cash_54103  trade_52209
3             NaN    0.000000    87.142349
4             NaN    0.000000     0.400000
5             NaN    1.000000   134.483871
13259    1.272331    0.016393          NaN
Run Code Online (Sandbox Code Playgroud)

数字似乎是正确的,但您可能希望将此结果集成到原始表中,并将NaN除以零错误的 s 替换为 0,因此让我们再次计算增量,但这次更漂亮,将列拆分为多重索引:

grouped = df.groupby(by=lambda l: '_'.join(map(str, l[:2])), axis=1)
increments = {k: calculate_incr(grp) for k, grp in grouped}
increments = pd.DataFrame(increments).fillna(0)
increments.columns = increments.columns.map(lambda c: tuple([*c.split('_'), 'YoY%']))
Run Code Online (Sandbox Code Playgroud)

啊哈!现在看起来更好了:

In []: increments
Out[]:
           cash                 trade
          54100     54103       52209
           YoY%      YoY%        YoY%
3      0.000000  0.000000   87.142349
4      0.000000  0.000000    0.400000
5      0.000000  1.000000  134.483871
13259  1.272331  0.016393    0.000000
Run Code Online (Sandbox Code Playgroud)

现在也可以轻松地将这些结果合并到您的原始数据中:

In []: result = pd.concat([df, increments], axis=1)

In []: result
Out[]:
0        cash                       trade              cash                 trade
1       54100          54103        52209             54100     54103       52209
2       02-21   03-21  02-21  03-21 02-21   03-21      YoY%      YoY%        YoY%
3           0       0   1500   1500 -28.1 -2476.8  0.000000  0.000000   87.142349
4           0     500  16500  16500    25      35  0.000000  0.000000    0.400000
5           0       0    300    600   3.1     420  0.000000  1.000000  134.483871
13259  1973.7  4484.9  18300  18600     0     750  1.272331  0.016393    0.000000
Run Code Online (Sandbox Code Playgroud)

最后,如果您想像您提供的示例中那样保留表格,不带标题(即多索引列),只需使用此技巧来重置列:

In []: result = result.columns.to_frame().T.append(result, ignore_index=True).T.reset_index(drop=True).T

In []: result
Out[]:
        0       1      2      3      4       5        6          7        8
0    cash    cash   cash   cash  trade   trade     cash       cash    trade
1   54100   54100  54103  54103  52209   52209    54100      54103    52209
2   02-21   03-21  02-21  03-21  02-21   03-21     YoY%       YoY%     YoY%
3       0       0   1500   1500  -28.1 -2476.8        0          0  87.1423
4       0     500  16500  16500     25      35        0          0      0.4
5       0       0    300    600    3.1     420        0          1  134.484
6  1973.7  4484.9  18300  18600      0     750  1.27233  0.0163934        0
Run Code Online (Sandbox Code Playgroud)

  • 感谢您在没有提供输入数据的情况下解决这个问题,+1。BTW pandas 如果转换为 float,将自动处理被零除: `return df.iloc[:,1].astype(float) / df.iloc[:,0].astype(float) - 1` (或只需预先将`df`转换为浮动) (2认同)