Python 熊猫:合并分层数据

Python 熊猫:合并分层数据,python,pandas,merge,dataframe,Python,Pandas,Merge,Dataframe,我正在寻找一种方法,将具有复杂层次结构的数据合并到pandasDataFrame。这种层次结构是由数据中不同的相互依赖关系产生的。例如,有一些参数定义了数据是如何产生的,然后有与时间相关的观测值、与空间相关的观测值以及与时间和空间相关的观测值 更明确地说:假设我有以下数据 # Parameters t_max = 2 t_step = 15 sites = 4 # Purely time-dependent t = np.linspace(0, t_max, t_step) f_t = t*

我正在寻找一种方法,将具有复杂层次结构的数据合并到pandas
DataFrame
。这种层次结构是由数据中不同的相互依赖关系产生的。例如,有一些参数定义了数据是如何产生的,然后有与时间相关的观测值、与空间相关的观测值以及与时间和空间相关的观测值

更明确地说:假设我有以下数据

#  Parameters
t_max = 2
t_step = 15
sites = 4

# Purely time-dependent
t = np.linspace(0, t_max, t_step)
f_t = t**2 - t

# Purely site-dependent
position = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])  # (x, y)
site_weight = np.arange(sites)

# Time-, and site-dependent.
occupation = np.arange(t_step*sites).reshape((t_step, sites))

# Time-, and site-, site-dependent
correlation = np.arange(t_step*sites*sites).reshape((t_step, sites, sites))
(当然,最后我会有很多这样的数据集,每一组参数对应一组数据。)

现在,我想把所有这些都隐藏到一个pandas
DataFrame
中。我想象最终的结果是这样的:

| ----- parameters ----- | -------------------------------- observables --------------------------------- |
|                        |                                        | ---------- time-dependent ----------- |
|                        | ----------- site-dependent --- )       ( ------------------------ |            |
|                        |                                | - site2-dependent - |                         |
| sites | t_max | t_step | site | r_x | r_y | site weight | site2 | correlation | occupation | f_t | time |
         parameters                 observables                                                                       
              sites  t_max  t_step         time       f_t  site  occupation  site2  correlation  r_x  r_y  site weight
    0             4      2      15     0.000000  0.000000     0           0      0            0    0    0            0
    1           NaN    NaN     NaN     0.000000  0.000000     0           0      1            1    0    0            0
    2           NaN    NaN     NaN     0.000000  0.000000     0           0      2            2    0    0            0
    3           NaN    NaN     NaN     0.000000  0.000000     0           0      3            3    0    0            0
    4           NaN    NaN     NaN     0.142857 -0.122449     0           4      0           16    0    0            0
    ..          ...    ...     ...          ...       ...   ...         ...    ...          ...  ...  ...          ...
    235         NaN    NaN     NaN     1.857143  1.591837     3          55      3          223    1    1            3
    236         NaN    NaN     NaN     2.000000  2.000000     3          59      0          236    1    1            3
    237         NaN    NaN     NaN     2.000000  2.000000     3          59      1          237    1    1            3
    238         NaN    NaN     NaN     2.000000  2.000000     3          59      2          238    1    1            3
    239         NaN    NaN     NaN     2.000000  2.000000     3          59      3          239    1    1            3
我认为部分重叠的层次结构可能无法实现。如果它们是隐式的,也没关系,从某种意义上说,我可以通过以特定方式索引数据帧来获取所有站点相关的数据

另外,如果您认为有更好的方式在熊猫中整理这些数据,请随时告诉我

问题: 如何构建包含上述所有数据并以某种方式反映相互依赖关系的
数据框
(例如
f\u t
取决于
时间
,而不是
站点
)。所有这些都是以一种足够通用的方式进行的,这样就可以很容易地添加或删除某些可观察到的对象,并且可能有新的相互依赖关系。(例如,取决于第二时间轴的量,如时间相关性。)


到目前为止我得到了什么 在下面,我将向你展示我自己已经走了多远。然而,我不认为这是实现上述目标的理想方式。特别是,因为它在添加或删除某些可观察对象方面缺乏通用性

指数 鉴于以上数据,我首先定义了我需要的所有多个索引

ind_time = pd.Index(t, name='time')
ind_site = pd.Index(np.arange(sites), name='site')
ind_site_site = pd.MultiIndex.from_product([ind_site, ind_site], names=['site', 'site2'])
ind_time_site = pd.MultiIndex.from_product([ind_time, ind_site], names=['time', 'site'])
ind_time_site_site = pd.MultiIndex.from_product([ind_time, ind_site, ind_site], names=['time', 'site', 'site2'])
单个
DataFrame
s 接下来,我创建了各个数据块的数据帧

df_parms = pd.DataFrame({'t_max': t_max, 't_step': t_step, 'sites': sites}, index=[0])
df_time = pd.DataFrame({'f_t': f_t}, index=ind_time)
df_position = pd.DataFrame(position, columns=['r_x', 'r_y'], index=ind_site)
df_weight = pd.DataFrame(site_weight, columns=['site weight'], index=ind_site)
df_occupation = pd.DataFrame(occupation.flatten(), index=ind_time_site, columns=['occupation'])
df_correlation = pd.DataFrame(correlation.flatten(), index=ind_time_site_site, columns=['correlation'])
df_parms
中的
索引=[0]
似乎是必要的,因为否则熊猫只会抱怨标量值。实际上,我可能会用这个特定模拟运行的时间戳来代替它。这至少会传达一些有用的信息

合并观测值 有了可用的数据帧,我将所有的可观测数据合并成一个大的
数据帧

df_all_but_parms = pd.merge(
  pd.merge(
    pd.merge(
      df_time.reset_index(),
      df_occupation.reset_index(),
      how='outer'
    ),
    df_correlation.reset_index(),
    how='outer'
  ),
  pd.merge(
    df_position.reset_index(),
    df_weight.reset_index(),
    how='outer'
  ),
  how='outer'
)
在我目前的方法中,这是我最不喜欢的一点。
merge
函数仅适用于成对的数据帧,它要求它们至少有一个公共列。因此,我必须小心连接数据帧的顺序,如果我要添加一个正交的可观测值,那么我不能将其与其他数据合并,因为它们不会共享一个公共列。是否有一个函数可以通过对数据帧列表的一次调用实现相同的结果?我尝试了
concat
,但它无法合并公共列。因此,我得到了大量重复的
time
site

合并所有数据 最后,我将数据与参数合并

pd.concat([df_parms, df_all_but_parms], axis=1, keys=['parameters', 'observables'])
到目前为止,最终结果如下:

| ----- parameters ----- | -------------------------------- observables --------------------------------- |
|                        |                                        | ---------- time-dependent ----------- |
|                        | ----------- site-dependent --- )       ( ------------------------ |            |
|                        |                                | - site2-dependent - |                         |
| sites | t_max | t_step | site | r_x | r_y | site weight | site2 | correlation | occupation | f_t | time |
         parameters                 observables                                                                       
              sites  t_max  t_step         time       f_t  site  occupation  site2  correlation  r_x  r_y  site weight
    0             4      2      15     0.000000  0.000000     0           0      0            0    0    0            0
    1           NaN    NaN     NaN     0.000000  0.000000     0           0      1            1    0    0            0
    2           NaN    NaN     NaN     0.000000  0.000000     0           0      2            2    0    0            0
    3           NaN    NaN     NaN     0.000000  0.000000     0           0      3            3    0    0            0
    4           NaN    NaN     NaN     0.142857 -0.122449     0           4      0           16    0    0            0
    ..          ...    ...     ...          ...       ...   ...         ...    ...          ...  ...  ...          ...
    235         NaN    NaN     NaN     1.857143  1.591837     3          55      3          223    1    1            3
    236         NaN    NaN     NaN     2.000000  2.000000     3          59      0          236    1    1            3
    237         NaN    NaN     NaN     2.000000  2.000000     3          59      1          237    1    1            3
    238         NaN    NaN     NaN     2.000000  2.000000     3          59      2          238    1    1            3
    239         NaN    NaN     NaN     2.000000  2.000000     3          59      3          239    1    1            3
正如您所看到的,这并不能很好地工作,因为实际上只有第一行被指定了参数。所有其他行仅使用
NaN
s代替参数。但是,由于这些是所有这些数据的参数,因此它们也应该包含在此数据框的所有其他行中

作为一个小问题:如果我将上面的数据帧存储在hdf5中,熊猫会有多聪明。我最终会得到大量重复数据,还是会避免重复存储


更新 多亏了,我能够通过通用合并将所有数据推送到一个数据帧中。基本思想是,我所有的观测值都已经有了一些公共列。即参数

pd.concat([df_parms, df_all_but_parms], axis=1, keys=['parameters', 'observables'])
首先,我将参数添加到所有可观察对象的数据帧中

all_observables = [ df_time, df_position, df_weight, df_occupation, df_correlation ]
flat = map(pd.DataFrame.reset_index, all_observables)
for df in flat:
    for c in df_parms:
        df[c] = df_parms.loc[0,c]
然后我可以通过归约将它们合并在一起

df_all = reduce(lambda a, b: pd.merge(a, b, how='outer'), flat)
其结果具有所需的形式:

         time       f_t  sites  t_max  t_step  site  r_x  r_y  site weight  occupation  site2  correlation
0    0.000000  0.000000      4      2      15     0    0    0            0           0      0            0
1    0.000000  0.000000      4      2      15     0    0    0            0           0      1            1
2    0.000000  0.000000      4      2      15     0    0    0            0           0      2            2
3    0.000000  0.000000      4      2      15     0    0    0            0           0      3            3
4    0.142857 -0.122449      4      2      15     0    0    0            0           4      0           16
5    0.142857 -0.122449      4      2      15     0    0    0            0           4      1           17
6    0.142857 -0.122449      4      2      15     0    0    0            0           4      2           18
..        ...       ...    ...    ...     ...   ...  ...  ...          ...         ...    ...          ...
233  1.857143  1.591837      4      2      15     3    1    1            3          55      1          221
234  1.857143  1.591837      4      2      15     3    1    1            3          55      2          222
235  1.857143  1.591837      4      2      15     3    1    1            3          55      3          223
236  2.000000  2.000000      4      2      15     3    1    1            3          59      0          236
237  2.000000  2.000000      4      2      15     3    1    1            3          59      1          237
238  2.000000  2.000000      4      2      15     3    1    1            3          59      2          238
239  2.000000  2.000000      4      2      15     3    1    1            3          59      3          239
通过重新索引数据,层次结构变得更加明显:

df_all.set_index(['t_max', 't_step', 'sites', 'time', 'site', 'site2'], inplace=True)
导致

                                             f_t  r_x  r_y  site weight  occupation  correlation
t_max t_step sites time     site site2                                                          
2     15     4     0.000000 0    0      0.000000    0    0            0           0            0
                                 1      0.000000    0    0            0           0            1
                                 2      0.000000    0    0            0           0            2
                                 3      0.000000    0    0            0           0            3
                   0.142857 0    0     -0.122449    0    0            0           4           16
                                 1     -0.122449    0    0            0           4           17
                                 2     -0.122449    0    0            0           4           18
...                                          ...  ...  ...          ...         ...          ...
                   1.857143 3    1      1.591837    1    1            3          55          221
                                 2      1.591837    1    1            3          55          222
                                 3      1.591837    1    1            3          55          223
                   2.000000 3    0      2.000000    1    1            3          59          236
                                 1      2.000000    1    1            3          59          237
                                 2      2.000000    1    1            3          59          238
                                 3      2.000000    1    1            3          59          239

我认为你应该这样做,把
df_parms
作为你的索引。通过这种方式,您可以轻松地使用不同的参数连接更多帧

In [67]: pd.set_option('max_rows',10)

In [68]: dfx = df_all_but_parms.copy()
您需要将列指定给框架(您也可以直接构造多索引,但这是从数据开始的)

设置索引(这将返回一个新对象)


你正试图在一个画面里推很多东西。想象一个多重索引(比如在索引上),枚举层次的笛卡尔积。你的数据是n维的吗?列中的多索引实际上只是一种标签约定。@Jeff感谢您的评论。我不确定我是否明白你在说什么。您是否建议我将这些内容存储在单独的数据帧中?或者,您是否建议我在最终表格中添加多个索引,以便更好地结构化数据?为什么不显示一些您希望执行的操作(什么类型的选择、数字操作等等)。如果你能给出一个输出示例,最好不要在你的框架中包含
df\u parms
df_all_但_parms
看起来也不错is@Jeff因为我还没有让数据帧工作,所以我还没有任何代码使用它。但是,从概念上来说,我想用它做些什么。首先,我想在绘图图例中将观测值绘制为
时间
的函数,并使用某些参数或站点索引。我还想用这些做一些基本的运算,例如,
动能+相互作用能
。或者,我想把站点的占有率加起来,通过站点权重进行加权,然后将其绘制为时间的函数。谢谢,将参数放入索引中可能是个好主意。你知道有没有更好的方法来构建
df\u all\u but\u parms
?理想情况下,只需对sequ进行一次调用