将带有元组条目的 DataFrame 解压到单独的 DataFrame 中

Unpack DataFrame with tuple entries into separate DataFrames

我写了一个小的 class 来通过 bootstrap 计算一些统计数据而无需替换。对于那些不熟悉这种技术的人,你会得到一些数据的 n 个随机子样本,计算每个子样本的所需统计量(比如说中位数),然后比较子样本之间的值。这允许您在数据集上获得的中位数获得方差度量。

我在 class 中实现了它,但将其简化为由以下函数给出的 MWE

import numpy as np
import pandas as pd

def bootstrap_median(df, n=5000, fraction=0.1):
    if isinstance(df, pd.DataFrame):
        columns = df.columns
    else:
        columns = None
    # Get the values as a ndarray
    arr = np.array(df.values)

    # Get the bootstrap sample through random permutations
    sample_len = int(len(arr)*fraction)
    if sample_len<1:
        sample_len = 1
    sample = []
    for n_sample in range(n):
        sample.append(arr[np.random.permutation(len(arr))[:sample_len]])
    sample = np.array(sample)

    # Compute the median on each sample
    temp = np.median(sample, axis=1)

    # Get the mean and std of the estimate across samples
    m = np.mean(temp, axis=0)
    s = np.std(temp, axis=0)/np.sqrt(len(sample))

    # Convert output to DataFrames if necesary and return
    if columns:
        m = pd.DataFrame(data=m[None, ...], columns=columns)
        s = pd.DataFrame(data=s[None, ...], columns=columns)
    return m, s

此函数 returns 每个 bootstrap 样本计算的中位数的平均值和标准差。

现在考虑这个例子DataFrame

data  = np.arange(20)
group = np.tile(np.array([1, 2]).reshape(-1,1), (1,10)).flatten()
df    = pd.DataFrame.from_dict({'data': data, 'group': group})
print(df)
print(bootstrap_median(df['data']))

这会打印

    data  group
0      0      1
1      1      1
2      2      1
3      3      1
4      4      1
5      5      1
6      6      1
7      7      1
8      8      1
9      9      1
10    10      2
11    11      2
12    12      2
13    13      2
14    14      2
15    15      2
16    16      2
17    17      2
18    18      2
19    19      2

(9.5161999999999995, 0.056585753613431718)

到目前为止一切顺利,因为 bootstrap_median returns 两个元素的 tuple。但是,如果我在 groupby

之后执行此操作
In: df.groupby('group')['data'].apply(bootstrap_median)

Out:
group
1     (4.5356, 0.0409710449952)
2    (14.5006, 0.0403772204095)

每个单元格内的值是 tuple,正如人们对 apply 的期望一样。我可以通过遍历这样的元素将结果解压缩为两个 DataFrame

index = []
data1 = []
data2 = []
for g, (m, s) in out.iteritems():
    index.append(g)
    data1.append(m)
    data2.append(s)
dfm = pd.DataFrame(data=data1, index=index, columns=['E[median]'])
dfm.index.name = 'group'
dfs = pd.DataFrame(data=data2, index=index, columns=['std[median]'])
dfs.index.name = 'group'

因此

In: dfm
Out:
       E[median]
group  
1         4.5356
2        14.5006

In: dfs
Out:
           std[median]
group  
1      0.0409710449952
2      0.0403772204095

这有点麻烦,我的问题是是否有更多 pandas 本地方法来 "unpack" 一个数据帧,其值是元组到单独的 DataFrame

似乎相关,但它涉及字符串正则表达式替换而不是解包真正的元组。

我认为你需要改变:

return m, s

至:

return pd.Series([m, s], index=['m','s'])

然后得到:

df1 = df.groupby('group')['data'].apply(bootstrap_median)
print (df1)
group   
1      m     4.480400
       s     0.040542
2      m    14.565200
       s     0.040373
Name: data, dtype: float64

select xs:

也是可能的
print (df1.xs('s', level=1))
group
1    0.040542
2    0.040373
Name: data, dtype: float64

print (df1.xs('m', level=1))
group
1     4.4804
2    14.5652
Name: data, dtype: float64

另外,如果需要一栏 DataFrame 添加 to_frame:

df1 = df.groupby('group')['data'].apply(bootstrap_median).to_frame()
print (df1)
              data
group             
1     m   4.476800
      s   0.041100
2     m  14.468400
      s   0.040719

print (df1.xs('s', level=1))
           data
group          
1      0.041100
2      0.040719

print (df1.xs('m', level=1))
          data
group         
1       4.4768
2      14.4684