Python 熊猫-转换数据视图

Python 熊猫-转换数据视图,python,numpy,pandas,Python,Numpy,Pandas,我有一个数据框,如下所示,其中id和date是索引 id name date gross1 gross2 net1 net2 balance1 balance2 1 abc 01/01/2001 100 101 50 51 200 201 2 def 01/02/2001 201 202 40 41 300

我有一个数据框,如下所示,其中id和date是索引

id      name    date        gross1  gross2  net1    net2    balance1    balance2
1       abc     01/01/2001  100     101     50      51      200         201 
2       def     01/02/2001  201     202     40      41      300         3001
3       ghi     01/03/2001  300     303     99      98      1000        10001
我希望对其进行转换,使数据按如下方式进行转换:

id  date        level   parent  category    name    value1  value1
1   01/01/2001  0       NaN     gross       abc     100     101
2   01/01/2001  1       1       net         abc     50      51
3   01/01/2001  1       1       balance     abc     200     201

4   01/02/2001  0       NaN     gross       def     201     201
5   01/02/2001  1       4       net         def     40      41
6   01/02/2001  1       4       balance     def     300     3001

7   01/03/2001  0       NaN     gross       ghi     300     303
8   01/03/2001  1       7       net         ghi     99      98
9   01/03/2001  1       7       balance     ghi     1000    10001
我试着旋转和卸垛……但没能完全正确。我来的时候做的事情如下:

df_gross = df['name','gross1','gross2']
df_gross.columns = ['name', 'value1', 'value2']
df_gross['level']=0
df_gross['category']='gross'

df_net = df['name', 'net1','net2']
df_net.columns = ['name', 'value1', 'value2']
df_gross['level']=1
df_gross['category']='net'

df_balance = df['name', 'balance1','balance2']
df_balance.columns = ['name', 'value1', 'value2']
df_balance['level']=1
df_balance['category']='balance'
df = pandas.concat(df_gross, df_net, df_balance)
然而,我面临的问题是如何高效地生成新id,并将父列设置为新生成的id。

我可以..在concat之后,重置_索引,然后删除'id'列,然后将索引设置为pandas生成的索引(应该是值1..n)。然后我想我做了一个dataframe.apply,通过'date'和'level=0'找到父对象,并相应地设置父对象。这是最有效的方法吗?

我认为最好的方法是通过pandas重塑索引和名称,并使用numpy重塑值

首先,让我们在numpy中重塑数值:

arr = df.ix[:,'gross1':'balance2'].values.reshape(9,2)

array([[  100,   101],
       [   50,    51],
       [  200,   201],
       [  201,   202],
       [   40,    41],
       [  300,  3001],
       [  300,   303],
       [   99,    98],
       [ 1000, 10001]], dtype=int64)
现在,让我们在pandas中重塑数据帧,以便索引和列名更接近我们想要的:

df2 = df.set_index(['id','date','name']).stack().iloc[::2].reset_index().iloc[:,:-1]

   id        date name   level_3
0   1  01/01/2001  abc    gross1
1   1  01/01/2001  abc      net1
2   1  01/01/2001  abc  balance1
3   2  01/02/2001  def    gross1
4   2  01/02/2001  def      net1
5   2  01/02/2001  def  balance1
6   3  01/03/2001  ghi    gross1
7   3  01/03/2001  ghi      net1
8   3  01/03/2001  ghi  balance1
这基本上是90%,现在只需将它们结合起来:

df2[['value1','value2']] = pd.DataFrame(arr)

   id        date name   level_3  value1  value2
0   1  01/01/2001  abc    gross1     100     101
1   1  01/01/2001  abc      net1      50      51
2   1  01/01/2001  abc  balance1     200     201
3   2  01/02/2001  def    gross1     201     202
4   2  01/02/2001  def      net1      40      41
5   2  01/02/2001  def  balance1     300    3001
6   3  01/03/2001  ghi    gross1     300     303
7   3  01/03/2001  ghi      net1      99      98
8   3  01/03/2001  ghi  balance1    1000   10001
我不确定您打算如何使用级别/父级Colunn,但以下是如何设置它们:

df2['parent'] = df2.groupby('id').cumcount()
df2['parent_index'] = df2[ df2.parent == 0 ].index.to_series()
df2['parent_index'] = df2.parent_index.fillna(method='ffill')
df2['parent'] = np.where( df2.parent > 1, 1, df2.parent )
df2['parent_index'] = np.where( df2.parent == 0, np.nan, df2.parent_index )

   id        date name   level_3  value1  value2  parent  parent_index
0   1  01/01/2001  abc    gross1     100     101       0           NaN
1   1  01/01/2001  abc      net1      50      51       1             0
2   1  01/01/2001  abc  balance1     200     201       1             0
3   2  01/02/2001  def    gross1     201     202       0           NaN
4   2  01/02/2001  def      net1      40      41       1             3
5   2  01/02/2001  def  balance1     300    3001       1             3
6   3  01/03/2001  ghi    gross1     300     303       0           NaN
7   3  01/03/2001  ghi      net1      99      98       1             6
8   3  01/03/2001  ghi  balance1    1000   10001       1             6

这完全可以用熊猫来完成

import numpy as np
import pandas as pd

# assuming your dataframe is called `df`, first stack the dataframe
dfnew = df.set_index(['id', 'date','name']).stack().reset_index()

# split the category information into category and value level, then delete column level_3
dfnew[['category', 'valuelevel']] = dfnew.level_3.apply(
        lambda x: pd.Series([x[:-1], x[-1]]))
del dfnew['level_3']

# reshape data to meet required format and reset_index
dfnew = dfnew.set_index(['id', 'date', 'name', 'category', 'valuelevel']).unstack(level=-1).reset_index()

# fix MultiIndex mess by flattening the column names, 
# note: renaming id to parent because that is what it will end up being, new id will be the index.
dfnew.columns = ['parent', 'date', 'name', 'category', 'value1', 'value2']

# reorder the data frame according to parent_id & category ['gross', 'net', 'balance'], 
# using a second data frame
# then get rid of the extra fields `index` & `catlevel`
cat_level = pd.DataFrame({'category': ['gross', 'net', 'balance'], 'catlevel': [0, 1, 2]})
dfnew = dfnew.merge(cat_level)
dfnew = dfnew.sort(['parent', 'catlevel']).reset_index()
del dfnew['index']
del dfnew['catlevel']

# generate the new row id from index
dfnew['id'] = dfnew.reset_index()['index'] + 1

# reset the parent column to point to the current parent id
dfnew['parent'] = dfnew.groupby('parent')['id'].transform(min)

# add new column level
dfnew['level'] = 1

# update the parent & level columns based on the mask parent == id
mask = dfnew.parent == dfnew.id
dfnew.level[mask] = 0
dfnew.parent[mask] = np.NaN
最后的数据帧如下所示:

   parent        date name category  value1  value2  id  level
0     NaN  01/01/2001  abc    gross     100     101   1      0
1       1  01/01/2001  abc      net      50      51   2      1
2       1  01/01/2001  abc  balance     200     201   3      1
3     NaN  01/02/2001  def    gross     201     202   4      0
4       4  01/02/2001  def      net      40      41   5      1
5       4  01/02/2001  def  balance     300    3001   6      1
6     NaN  01/03/2001  ghi    gross     300     303   7      0
7       7  01/03/2001  ghi      net      99      98   8      1
8       7  01/03/2001  ghi  balance    1000   10001   9      1
列顺序与您指定的不同,但形状和值正确。我不知道如何移动列,但我们可以轻松创建具有正确列顺序的新数据帧

column_ordered = ['id', 'date', 'level', 'parent', 'category', 'name', 'value1', 'value2']
finaldf = pd.DataFrame()
for col in columns_ordered:
    finaldf[col] = dfnew[col]

为什么第一个家长ID是NaN?这是需要的吗?是..空..表示顶层。我想我可以自己做。我刚刚注意到“可信和/或官方消息来源”的说明。不确定这意味着什么,但我的答案中的所有内容都是numpy/pandas的标准用法。
父行
是否必须是带有
category=gross
的那一行?谢谢。我很好奇,基于我迄今为止所取得的成就,我将如何做到这一点?通过选择相关列将数据帧拆分为3个数据帧。考虑到这一点,我如何才能以我想要的方式生成ID和父ID?我不确定我是否遵循了这个问题,但可以说两件事:(1)通常,最好将相关的东西保持在同一个数据帧中,而不是将它们分开。(2) 您可能希望发布一个新的后续问题,确切地显示您希望如何处理此数据。有时,最好是询问如何在保留pandas中的方法的同时获得一些结果。也就是说,您可以使用类似于
df_-gross=df[['id'、'date'、'name'、'gross1'、'gross2']]
df_-gross=df2[df2.level_3='gross1']
的内容剥离原始数据集中的“gross”。但是我怀疑你最好把东西放在一起,使用
groupby('id')
进行处理——不过我也不知道你到底想在这里做什么。现在发生的是,数据出现在一个包含43列的大表中!前3列可视为关键,以下40列可分为4类,每类10列可叠加在其他列上(即10列显示世界数据,10列显示北美数据,10列显示欧洲数据,10列显示亚洲数据)。我想做的是在树状视图网格中这样显示它们..其中“世界”数据是父数据,如果用户需要,它们可以展开以查看细分。嗯,您可以选择。如果我是你,我会发布一个新问题,但你可以将所有内容都保存在同一个数据集中,并使用选择技术,或者每次都将它们分开然后重新合并。您可能希望在每一行中保留重复的父数据,但如果您的数据不是那么大,这并不是一个真正的问题,并且可能会简化一些事情(这样您就不必每次都重新合并)。我推测这就是为什么可能最好只做一个后续问题并准确显示您想要的内容。