在没有真实索引的情况下重塑 pandas 数据框

Reshaping a pandas dataframe without a true index

过去几周我一直在尝试构建回归输出的数据框,并且我已经完成了大部分工作。我现在正尝试围绕一个列中的某些关键字重塑它,据我所知这是不可调用的。

我的数据框的简化版本如下所示:

?           coef pval  se   rsq
Intercept   1    0     .1   .1
Cash        2    0.2   .05  .1
Food        2    0.05  .2   .1
Intercept   3    0     .1   .2
Cash        1    0.01  .2   .2
Food        2    0.3   .1   .2
Zone        1    0.4   .3   .2

我想要实现的是:

                (1)      (2)
Intercept coef   1        3
Intercept pval   0        0
Intercept se     0.1      0.1
Cash coef        2        1
Cash pval        0.2      0.01
Cash se          0.05     0.2
Food coef        2        2
Food pval        0.05     0.3
Food se          0.2      0.1
Zone coef        NaN      1
Zone pval        NaN      0.4
Zone se          NaN      0.3
rsq              0.1      0.2

到目前为止,我已经尝试了几种方法,有希望使用 r 平方 (rsq) 作为索引进行重塑 -> RegTable = RegTable.pivot(index='rsq', columns=['pval', 'coef', 'robust_se'])

然而,这 returns 错误 ValueError: all arrays must be same length。一些研究让我认为这是因为截至目前,zone = NaN 由简单地没有区域行的回归表示,但我不确定如何修复它。此外,我一直无法弄清楚如何调用我标识为“?”的列。使用 PANDAS - 它未在 CSV 输出中标记。此外,这种方法似乎有问题,因为如果两个回归具有相同的 r 平方,它最终将抛出一个新的错误或对每个值进行平均,这两种情况都不是完全可取的。

此解决方案背后的直觉是将数据帧分成大致相等的两个部分。这里的假设是您只有两组数据点,因此这变得易于管理。

print(df)

           coef  pval    se  rsq
Intercept     1  0.00  0.10  0.1
Cash          2  0.20  0.05  0.1
Food          2  0.05  0.20  0.1
Intercept     3  0.00  0.10  0.2
Cash          1  0.01  0.20  0.2
Food          2  0.30  0.10  0.2
Zone          1  0.40  0.30  0.2


df_ = df.reset_index().iloc[:, :-1]

df2 = df_.iloc[df_['index'].drop_duplicates(keep='first').to_frame().index]
df1 = df_.iloc[df_['index'].drop_duplicates(keep='last')\
                 .to_frame().index.difference(df2.index)]

完成此操作后,必须堆叠每个部分,然后沿第一个轴连接起来。

out = pd.concat([df1.set_index('index').stack(),\
                 df2.set_index('index').stack()], 1)
out.append(pd.DataFrame([df.rsq.unique()], index=[('rsq', '')]))
out.columns = ['1', '2']

print(out) 

                   1     2
index                     
Cash      coef  1.00  2.00
          pval  0.01  0.20
          se    0.20  0.05
Food      coef  2.00  2.00
          pval  0.30  0.05
          se    0.10  0.20
Intercept coef  3.00  1.00
          pval  0.00  0.00
          se    0.10  0.10
Zone      coef   NaN  1.00
          pval   NaN  0.40
          se     NaN  0.30
rsq             0.10  0.20

让我们试试这个:

df.set_index(['rsq','?']).stack().unstack([0]).T\
  .reset_index().T.rename_axis([None,None])\
  .rename(columns={0:'(1)',1:'(2)'})\
  .sort_index()

哪里 df:

           ?  coef  pval    se  rsq
0  Intercept     1  0.00  0.10  0.1
1       Cash     2  0.20  0.05  0.1
2       Food     2  0.05  0.20  0.1
3  Intercept     3  0.00  0.10  0.2
4       Cash     1  0.01  0.20  0.2
5       Food     2  0.30  0.10  0.2
6       Zone     1  0.40  0.30  0.2

输出:

                 (1)   (2)
Cash      coef  2.00  1.00
          pval  0.20  0.01
          se    0.05  0.20
Food      coef  2.00  2.00
          pval  0.05  0.30
          se    0.20  0.10
Intercept coef  1.00  3.00
          pval  0.00  0.00
          se    0.10  0.10
Zone      coef   NaN  1.00
          pval   NaN  0.40
          se     NaN  0.30
rsq             0.10  0.20

这里有一种稍微古怪的方法,可以在不拆分成两个数据帧的情况下执行此操作。

此解决方案重命名索引以跟踪它们所属的回归,在缺少字段时添加 NaN(如 Zone 的情况)。
然后 groupbystack,并将列表列拆分为 (1)(2) 列(尽管它被概括为处理数据中发生的尽可能多的回归)。

df为:

            coef pval  se   rsq
Intercept   1    0     .1   .1
Cash        2    0.2   .05  .1
Food        2    0.05  .2   .1
Intercept   3    0     .1   .2
Cash        1    0.01  .2   .2
Food        2    0.3   .1   .2
Zone        1    0.4   .3   .2

将索引值重命名为 Intercept0Intercept1 等:

measures = df.index.unique()
found = {m:0 for m in measures}

for i, name in enumerate(df.index):
    if np.max(list(found.values())) > found[name]+1:
        df.loc["{}{}".format(name, found[name])] = np.nan
        found[name] += 1
    df.index.values[i] = "{}{}".format(name, found[name])
    found[name] += 1

df
            coef  pval    se  rsq
Intercept0   1.0  0.00  0.10  0.1
Cash0        2.0  0.20  0.05  0.1
Food0        2.0  0.05  0.20  0.1
Intercept1   3.0  0.00  0.10  0.2
Cash1        1.0  0.01  0.20  0.2
Food1        2.0  0.30  0.10  0.2
Zone1        1.0  0.40  0.30  0.2
Zone0        NaN   NaN   NaN  NaN

现在排列行,以便将每个回归的元素组合在一起。 (这主要是为了在正确的位置获得 NaN 行。)

order_by_reg = sorted(df.index, key=lambda x: ''.join(reversed(x)))
df = df.loc[order_by_reg]

df
            coef  pval    se  rsq
Food0        2.0  0.05  0.20  0.1
Zone0        NaN   NaN   NaN  NaN
Cash0        2.0  0.20  0.05  0.1
Intercept0   1.0  0.00  0.10  0.1
Food1        2.0  0.30  0.10  0.2
Zone1        1.0  0.40  0.30  0.2
Cash1        1.0  0.01  0.20  0.2
Intercept1   3.0  0.00  0.10  0.2

最后,groupbystack,并将列表的结果列拆分为 apply(pd.Series):

gb = (df.groupby(lambda x: x[:-1])
        .agg(lambda x: list(x))
        .stack()
        .apply(lambda pair: pd.Series({"({})".format(i):el for i, el in enumerate(pair)})))

gb
                 (0)   (1)
Cash      coef  2.00  1.00
          pval  0.20  0.01
          se    0.05  0.20
          rsq   0.10  0.20
Food      coef  2.00  2.00
          pval  0.05  0.30
          se    0.20  0.10
          rsq   0.10  0.20
Intercept coef  1.00  3.00
          pval  0.00  0.00
          se    0.10  0.10
          rsq   0.10  0.20
Zone      coef   NaN  1.00
          pval   NaN  0.40
          se     NaN  0.30
          rsq    NaN  0.20