【问题标题】:pandas - transform data viewpandas - 转换数据视图
【发布时间】:2015-04-01 03:03:16
【问题描述】:

我有如下图所示的 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         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 之后,reset_index,然后删除“id”列,然后将索引设置为 pandas 生成的索引(应该是值 1..n)。然后我在想我做一个dataframe.apply通过'date'和'level = 0'找到父级并相应地设置父级。那会是最有效的方法吗?

【问题讨论】:

  • 为什么第一个父ID是NaN?这是想要的吗?
  • 是..null..表示顶级。我想我可以自己做。
  • 我刚刚注意到“可信和/或官方消息来源”说明。不知道这意味着什么,但我的回答中的所有内容都是 numpy/pandas 的标准用法。
  • parent row 必须是category = gross 的那个吗?

标签: python numpy pandas


【解决方案1】:

我能想到的最好方法是通过 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

我不确定您打算如何使用级别/父列,但您可以通过以下方式设置它们:

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

【讨论】:

  • 谢谢..我很好奇根据我迄今为止所取得的成就如何做到这一点?通过选择相关列将数据框拆分为 3 个数据框。鉴于此,我如何以我想要的方式生成 id 和父 id?
  • 我不确定我是否遵循了这个问题,但可以说两件事:(1)通常最好将相关的东西放在同一个数据框中,而不是把它们分开。 (2) 您可能想要发布一个新的后续问题,以准确显示您希望如何处理这些数据。有时最好问问如何实现一些结果,同时让 pandas 中的方法有点开放。
  • 也就是说,您可以使用df_gross = df[['id','date','name','gross1','gross2']]df_gross = df2[ df2.level_3 == 'gross1'] 之类的内容剥离原始数据集中的“毛”。但我怀疑你最好把事情放在一起并使用groupby('id') 进行处理——尽管我也不知道你想在这里做什么。
  • 发生的事情是数据出现在一个有 43 列的大表中!前 3 列可以被认为是关键,接下来的 40 列可以分为 4 类,每列 10 列可以堆叠在一起(即 10 列显示世界数据,10 列表示北美,10 列cols 代表欧洲,10 列代表亚洲)。我想要做的是在树视图网格中显示它们......其中“世界”数据是父级,如果是用户案例,他们可以展开以查看故障。
  • 嗯,你有选择。如果我是您,我只会发布一个新问题,但是您可以将所有内容保存在同一个数据集中并使用选择技术,也可以分开然后每次重新合并。您可能希望将父数据保留在 每一行 中,这是重复的,但如果您的数据不是那么大并不是真正的问题,并且可能会简化一些事情(这样您就不必重新每次都合并。我推测一下,这就是为什么最好只做一个后续问题并准确显示你想要的。
【解决方案2】:

这完全可以只使用 pandas 来完成。

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]

【讨论】:

    猜你喜欢
    • 2012-10-15
    • 1970-01-01
    • 2018-05-11
    • 2019-01-20
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    相关资源
    最近更新 更多