从原始多索引创建新的 pandas 多索引 df

Making new pandas multiindex df from original multiindex

提问人:AndysPythonStuff 提问时间:10/25/2023 最后编辑:AndysPythonStuff 更新时间:10/26/2023 访问量:63

问:

我有一个多索引数据帧 df1,它是使用 yfinance 从雅虎提取的,带有时间序列索引和两级多索引列。级别 0 是“调整收盘价”、“最高价”和“成交量”,级别 1 是公司代码列表,如下所示:

调整关闭
C1级 C2型 C3型 C1级 C2型 C3型 C1级 C2型 C3型
日期
02-01-2020 12.78 41.73 24.03 13.50 41.77 26.43 100000 1234300 23454
03-01-2020 12.29 41.11 23.61 12.77 42.09 23.99 100022 1555555 23999

我试图通过对 df2 的列进行计算来制作一个新的数据帧 df1。

例如,使用相同的时间序列索引,Id 喜欢将新数据帧中的第一列 df2 设置为 df1 的(0 级)价格中每个(1 级)交易品种的百分比增长。然后 df2 中的是价格(级别 0)每个交易品种(级别 1)的滚动移动平均线。如果今天的最高价高于昨天的调整收盘价,则该列也会写入布尔值 True。

基本上,df2 中的每一都将通过操作 df1 中相应的 1 级公司符号的 0 级数据来制作。

(我说列我现在知道它不是列,而是按级别 0 分组的一组

于是我写了一个函数:

def indicators_df(df1):
    
    # Create a new DataFrame with the same index as df1
    df2 = pd.DataFrame(index=df1.index)
           
    # Plus / minus change %
    df2['Pct'] = df1['Adj Close'].pct_change().fillna(0)
    
    # Price MAs
    df2['$10MA'] = df1['Adj Close'].rolling(window=10).mean()
    df2['$20MA'] = df1['Adj Close'].rolling(window=20).mean()
    df2['$50MA'] = df1['Adj Close'].rolling(window=50).mean()
    df2['$100MA'] = df1['Adj Close'].rolling(window=100).mean()
    df2['$200MA'] = df1['Adj Close'].rolling(window=200).mean()
    
    # Volume MAs
    df2['V10MA'] = df1['Volume'].rolling(window=10).mean()
    df2['V20MA'] = df1['Volume'].rolling(window=20).mean()
    df2['V50MA'] = df1['Volume'].rolling(window=50).mean()
    df2['V100MA'] = df1['Volume'].rolling(window=100).mean()
    df2['V200MA'] = df1['Volume'].rolling(window=200).mean()
    
    return df2

这将返回以下错误:

ValueError: Cannot set a DataFrame with multiple columns to the single column Pct

我尝试了很多变化,但一直得到同样的错误,直到我意识到(我认为......我试图将所有 1 级公司符号的结果放在新数据帧 df2 的一列中,这不是多索引。

那么我将如何正确编写函数,创建一个新的多索引数据帧,其中 0 级值/键(不确定正确的术语)是 Ive 在 df1 上进行的计算,例如百分比变化、滚动平均值等?

谢谢。

Python Pandas DataFrame 多索引

评论


答:

0赞 Aymen Azoui 10/25/2023 #1

试试这个:

import pandas as pd

def indicators_df(df1):
    
    df2 = pd.DataFrame(index=df1.index)

    for col in df1.columns.levels[1]:
        
        df2[('Pct', col)] = df1['Adj Close', col].pct_change().fillna(0)
        
        df2[('$10MA', col)] = df1['Adj Close', col].rolling(window=10).mean()
        df2[('$20MA', col)] = df1['Adj Close', col].rolling(window=20).mean()
        df2[('$50MA', col)] = df1['Adj Close', col].rolling(window=50).mean()
        df2[('$100MA', col)] = df1['Adj Close', col].rolling(window=100).mean()
        df2[('$200MA', col)] = df1['Adj Close', col].rolling(window=200).mean()
        
        df2[('V10MA', col)] = df1['Volume', col].rolling(window=10).mean()
        df2[('V20MA', col)] = df1['Volume', col].rolling(window=20).mean()
        df2[('V50MA', col)] = df1['Volume', col].rolling(window=50).mean()
        df2[('V100MA', col)] = df1['Volume', col].rolling(window=100).mean()
        df2[('V200MA', col)] = df1['Volume', col].rolling(window=200).mean()
        
    return df2

评论

0赞 AndysPythonStuff 10/26/2023
谢谢。直觉上,这是 Id 尝试过的方法(如果我知道怎么做!我使用了您的代码,它产生了以下结果:空 DataFrame 列:[] 索引:[02-01-2020,03-01-2020,然后是红色页面:PerformanceWarning:DataFrame 高度碎片化。这通常是多次调用的结果,性能很差。请考虑改用 pd.concat(axis=1) 一次联接所有列。要获取去碎片化的帧,请使用 'newframe = frame.copy()frame.insert
1赞 Timeless 10/26/2023 #2

IIUC,作为一种简单的方法,您可以在预定义的计算对上连接多个 x

def indicators_df(df):
    def xs(df, m):
        return df.xs(m, axis=1, drop_level=False)

    def rn(df, d):
        return df.rename(d, axis=1, level=0)

    pct = (df.pipe(xs, "Adj Close").pct_change()
           .fillna(0).pipe(rn, {"Adj Close": "Pct"}))

    tmp = df.pipe(xs, "High")
    chk = tmp.eq(tmp.shift()).pipe(rn, {"High": "Check"})
    
    _map = {k: [(w, f"{pre}{w}MA") for w in [10, 20, 50, 100, 200]]
            for k, pre in [("Adj Close", "$"), ("Volume", "V")]}

    pvs = [df.pipe(xs, l0).rolling(b).mean().pipe(rn, {l0:l1})
           for (l0, p) in _map.items() for (b,l1) in p]

    return pd.concat([pct, chk, *pvs], axis=1)

输出:

print(indicators_df(df))

             Pct              Check         ... V100MA     V200MA        
              C1    C2    C3     C1     C2  ...     C2  C3     C1  C2  C3
Date                                        ...                          
02-01-2020  0.00  0.00  0.00  False  False  ...    NaN NaN    NaN NaN NaN
03-01-2020 -0.04 -0.01 -0.02  False  False  ...    NaN NaN    NaN NaN NaN

[2 rows x 36 columns]

使用的输入 :

df = pd.DataFrame.from_dict(
    {'index': ['02-01-2020', '03-01-2020'],
     'columns': [
         ('Adj Close', 'C1'), ('Adj Close', 'C2'), ('Adj Close', 'C3'),
         ('High', 'C1'), ('High', 'C2'), ('High', 'C3'), ('Volume', 'C1'),
         ('Volume', 'C2'),('Volume', 'C3')],
     'data': [[12.78, 41.73, 24.03, 13.5, 41.77,
               26.43, 100000, 1234300, 23454],
              [12.29, 41.11, 23.61, 12.77, 42.09,
               23.99, 100022, 1555555, 23999]],
     'index_names': ['Date'],
     'column_names': [None, None]}, orient='tight')

评论

0赞 AndysPythonStuff 10/26/2023
谢谢。。。我尝试了我的代码,并打开了一个新笔记本,两次我都得到:NameError:名称“i”未定义。我换了k,得到了一个结果。然而。。。你的代码比我的水平高出很多,所以我要一行一行地看一遍。毫无疑问,我学到了很多东西。谢谢!
0赞 Timeless 10/26/2023
哎呀,我们需要改成.我更新了我的答案。请随时提出任何问题;)iw
0赞 AndysPythonStuff 10/27/2023
再次感谢。我还不假装理解你的代码,而且 pvs 超出了我目前的理解水平,但我一直在使用它,我确实有问题......1)你定义了函数xs(df,m),但是当你稍后调用它时,df.pipe(xs, “Adj Close”).pct_change() 你没有指定任何参数?为什么?另外,我不知道pipe()...2) 在 pct 中,您管道了 2 个函数,这似乎是这个想法,但在 tmp 和 chk 中,您没有......有什么理由吗?我问是因为 df.xs(“High”, axis=1) 似乎与 df.pipe(xs, “High”) 相同。谢谢。。。