Pandas:我该如何将某些列转换为行?

11

假设我有以下的df。我想将价格列和数量列合并,以便所有价格在一列中,所有数量在另一列中。我还想要第三列来确定价格级别。例如,unit1unit2unit3

结果为:

假设我有以下的df。我想将价格列和数量列合并,以便所有价格在一列中,所有数量在另一列中。我还想要第三列来确定价格级别。例如,unit1unit2unit3

import numpy as np
import pandas as pd
df = pd.DataFrame(
    {
        'uid': ['U100', 'U200', 'E100', 'E200', 'E300', 'A100', 'A200', 'A300', 'A400', 'A500'],
        'location': ['US', 'US', 'EU', 'EU', 'EU', 'Asia', 'Asia', 'Asia', 'Asia', 'Asia'],
        'unit1_price': [10, 20, 15, 10, 10, 10, 20, 20, 25, 25],
        'unit1_vol': [100, 150, 100, 200, 150, 150, 100, 200, 200, 200],
        'unit2_price': [10, 25, 30, 20, 10, 10, 10, 10, 20, 20],
        'unit2_vol': [200, 200, 150, 300, 300, 200, 150, 225, 225, 250],
        'unit3_price': [0, 0, 0, 20, 20, 20, 20, 20, 20, 20],
        'unit3_vol': [0, 0, 0, 500, 500, 500, 500, 500, 500, 500]
    }
)
df

df1

这就是最终的df应该看起来的样子:

df_final


我尝试使用了melt,我认为几乎已经得到了正确的答案。

pd.melt(df, id_vars=['uid', 'location'], value_vars=['unit1_price', 'unit1_vol', 'unit2_price', 'unit2_vol', 'unit3_price', 'unit3_vol'])

使用 melt 函数后,这是部分数据框的样子:

df2

上述问题在于“volume”和“price”位于同一列中,但我希望它们分别位于两个不同的列中。

我是否使用了正确的函数?


1
为什么在提问时不应上传代码/数据/错误的图片?为什么不鼓励上传文本、代码和数学表达式的图片? - philipxy
@philipxy 这些是输出图像,而不是代码 - 我提供了所有的代码来重现我所做的事情。那么有人怎么知道解决方案应该是什么样子的呢?仅仅描述是不够的。我认为这就是插入图像功能存在的原因:https://i.imgur.com/6suQFhs.png - IamWarmduscher
7个回答

7
尝试使用melt,然后在split后进行pivot
s = df.melt(['uid','location'])
s[['unit','type']] = 

s['variable'].str.split('_',expand=True)
s = s.pivot(index = ['uid','location','unit'],columns = ['type'],values = 'value').reset_index()
s
Out[967]: 
type   uid location   unit  price  vol
0     A100     Asia  unit1     10  150
1     A100     Asia  unit2     10  200
2     A100     Asia  unit3     20  500
3     A200     Asia  unit1     20  100
4     A200     Asia  unit2     10  150

不幸的是,它没有起作用。它实际上看起来和我上面所做的一样。https://i.imgur.com/xV8taP7.png - IamWarmduscher
1
@IamWarmduscher,请查看更新。 - BENY
1
融合后变量的良好使用。 - SomeDude

4

您可以使用pyjanitor中的pivot_longer函数一步将宽格式转换为长格式,从而高效地完成转换:

# pip install pyjanitor
import pandas as pd
import janitor

(df
.pivot_longer(
     index = ['uid', 'location'], 
     names_to = ('unit', '.value'), 
     names_sep = '_')
)
     uid location   unit  price  vol
0   U100       US  unit1     10  100
1   U200       US  unit1     20  150
2   E100       EU  unit1     15  100
3   E200       EU  unit1     10  200
4   E300       EU  unit1     10  150
5   A100     Asia  unit1     10  150
6   A200     Asia  unit1     20  100
7   A300     Asia  unit1     20  200
8   A400     Asia  unit1     25  200
9   A500     Asia  unit1     25  200
10  U100       US  unit2     10  200
11  U200       US  unit2     25  200
12  E100       EU  unit2     30  150
13  E200       EU  unit2     20  300
14  E300       EU  unit2     10  300
15  A100     Asia  unit2     10  200
16  A200     Asia  unit2     10  150
17  A300     Asia  unit2     10  225
18  A400     Asia  unit2     20  225
19  A500     Asia  unit2     20  250
20  U100       US  unit3      0    0
21  U200       US  unit3      0    0
22  E100       EU  unit3      0    0
23  E200       EU  unit3     20  500
24  E300       EU  unit3     20  500
25  A100     Asia  unit3     20  500
26  A200     Asia  unit3     20  500
27  A300     Asia  unit3     20  500
28  A400     Asia  unit3     20  500
29  A500     Asia  unit3     20  500

.value 确定哪部分列保留为标题(在本例中是价格和成交量),而与 .value 无关的部分进入 unit 列。 names_sep 帮助拆分标签。

另一个选项是使用 pd.wide_to_long,它也允许在单个步骤中进行重塑。它需要对列进行一些处理:

i = ['uid', 'location']

temp = df.set_index(i)

# reshape the columns, moving price and vol to the front
temp.columns = temp.columns.str.split('_').str[::-1].str.join('_')

(pd
.wide_to_long(
    temp.reset_index(), 
    i = i, 
    j = 'unit', 
    stubnames = ['price', 'vol'], 
    sep='_', 
    suffix = '.+')
.reset_index()
)
     uid location   unit  price  vol
0   U100       US  unit1     10  100
1   U100       US  unit2     10  200
2   U100       US  unit3      0    0
3   U200       US  unit1     20  150
4   U200       US  unit2     25  200
5   U200       US  unit3      0    0
6   E100       EU  unit1     15  100
7   E100       EU  unit2     30  150
8   E100       EU  unit3      0    0
9   E200       EU  unit1     10  200
10  E200       EU  unit2     20  300
11  E200       EU  unit3     20  500
12  E300       EU  unit1     10  150
13  E300       EU  unit2     10  300
14  E300       EU  unit3     20  500
15  A100     Asia  unit1     10  150
16  A100     Asia  unit2     10  200
17  A100     Asia  unit3     20  500
18  A200     Asia  unit1     20  100
19  A200     Asia  unit2     10  150
20  A200     Asia  unit3     20  500
21  A300     Asia  unit1     20  200
22  A300     Asia  unit2     10  225
23  A300     Asia  unit3     20  500
24  A400     Asia  unit1     25  200
25  A400     Asia  unit2     20  225
26  A400     Asia  unit3     20  500
27  A500     Asia  unit1     25  200
28  A500     Asia  unit2     20  250
29  A500     Asia  unit3     20  500

您甚至可以使用堆栈选项,所有这些选项都是为了让您更有效地进行转换:

i = ['uid', 'location']

temp = df.set_index(i)

# create a MultiIndex
temp.columns = temp.columns.str.split('_', expand = True)
temp.columns.names = ['unit', None]

temp.stack('unit').reset_index()

     uid location   unit  price  vol
0   U100       US  unit1     10  100
1   U100       US  unit2     10  200
2   U100       US  unit3      0    0
3   U200       US  unit1     20  150
4   U200       US  unit2     25  200
5   U200       US  unit3      0    0
6   E100       EU  unit1     15  100
7   E100       EU  unit2     30  150
8   E100       EU  unit3      0    0
9   E200       EU  unit1     10  200
10  E200       EU  unit2     20  300
11  E200       EU  unit3     20  500
12  E300       EU  unit1     10  150
13  E300       EU  unit2     10  300
14  E300       EU  unit3     20  500
15  A100     Asia  unit1     10  150
16  A100     Asia  unit2     10  200
17  A100     Asia  unit3     20  500
18  A200     Asia  unit1     20  100
19  A200     Asia  unit2     10  150
20  A200     Asia  unit3     20  500
21  A300     Asia  unit1     20  200
22  A300     Asia  unit2     10  225
23  A300     Asia  unit3     20  500
24  A400     Asia  unit1     25  200
25  A400     Asia  unit2     20  225
26  A400     Asia  unit3     20  500
27  A500     Asia  unit1     25  200
28  A500     Asia  unit2     20  250
29  A500     Asia  unit3     20  500

3
现有的答案都很好。这里是另一种使用numpy的方法。只要 _price_vol 列的位置与 OP 中显示的完全交替 (因为它依赖于 numpy.reshape 将宽数据转换为长数据,所以在重塑时不会识别列名)。
# filter columns containing price or vol
price_vol_cols = df.columns.str.contains('price|vol')
# the number of vol columns (and price columns)
width = price_vol_cols.sum()//2
# repeat uid and location columns
res = pd.DataFrame(np.tile(df.loc[:, ~price_vol_cols], (width, 1)), columns=df.columns[~price_vol_cols])
# repeat price and vol column names 
res['unit'] = np.repeat(df.columns[price_vol_cols].str.split('_').str[0], len(df)//2)
# reshape price and vol columns into 2 columns by stacking every 2 columns
res[['price', 'vol']] = np.vstack(df.loc[:, price_vol_cols].values.reshape(-1, width, 2).swapaxes(0,1))

enter image description here


3

也许是这样:

import pandas as pd
df = pd.DataFrame(
    {
        'uid': ['U100', 'U200', 'E100', 'E200', 'E300', 'A100', 'A200', 'A300', 'A400', 'A500'],
        'location': ['US', 'US', 'EU', 'EU', 'EU', 'Asia', 'Asia', 'Asia', 'Asia', 'Asia'],
        'unit1_price': [10, 20, 15, 10, 10, 10, 20, 20, 25, 25],
        'unit1_vol': [100, 150, 100, 200, 150, 150, 100, 200, 200, 200],
        'unit2_price': [10, 25, 30, 20, 10, 10, 10, 10, 20, 20],
        'unit2_vol': [200, 200, 150, 300, 300, 200, 150, 225, 225, 250],
        'unit3_price': [0, 0, 0, 20, 20, 20, 20, 20, 20, 20],
        'unit3_vol': [0, 0, 0, 500, 500, 500, 500, 500, 500, 500]
    }
)

price = pd.melt(
    df, id_vars=['uid', 'location', 'unit2_vol', 'unit1_vol', 'unit3_vol'], value_vars=['unit1_price', 'unit3_price', 'unit2_price'], var_name="price", value_name="price_value"
)

res = pd.melt(
    price, id_vars=['uid', 'location', 'price', 'price_value'], value_vars=['unit2_vol', 'unit1_vol', 'unit3_vol'], var_name="vol", value_name="vol_value"
)
print(res)

输出:

     uid location        price  price_value        vol  vol_value
0   U100       US  unit1_price           10  unit2_vol        200
1   U200       US  unit1_price           20  unit2_vol        200
2   E100       EU  unit1_price           15  unit2_vol        150
3   E200       EU  unit1_price           10  unit2_vol        300
4   E300       EU  unit1_price           10  unit2_vol        300
..   ...      ...          ...          ...        ...        ...
85  A100     Asia  unit2_price           10  unit3_vol        500
86  A200     Asia  unit2_price           10  unit3_vol        500
87  A300     Asia  unit2_price           10  unit3_vol        500
88  A400     Asia  unit2_price           20  unit3_vol        500
89  A500     Asia  unit2_price           20  unit3_vol        500

3
你可以使用pd.melt来形成两个数据框,然后将它们组合成一个数据框。
df1 = df.melt(id_vars=['uid', 'location'], value_vars=['unit1_price', 'unit2_price', 'unit3_price'],var_name='unit',value_name='price')

df2 = df.melt(id_vars=['uid', 'location'], value_vars=['unit1_vol', 'unit2_vol', 'unit3_vol'],var_name='unit', value_name="volume")

ddf = pd.concat([df1,df2['volume']],axis=1).sort_values(by=['uid','unit'],ignore_index=True)

ddf['unit']=ddf['unit'].str.split('_',expand=True)[0]

3
你可以做到:
df_price = df.set_index(['uid','location']).filter(
    regex='price$').stack().rename_axis(
    ['uid', 'location', 'price_unit']).rename('price').reset_index()

df_vol = df.filter(regex='vol$').stack().rename_axis(
    ['', 'vol_unit']).rename('volume').reset_index(level=1).reset_index(drop=True)

df2 = pd.concat([df_price, df_vol], axis=1)
df2['unit'] = df2['price_unit'].apply(lambda x:x.split('_')[0])
df2.drop(['price_unit', 'vol_unit'],axis=1, inplace=True)

打印(输出)df2:

     uid location  price  volume   unit
0   U100       US     10     100  unit1
1   U100       US     10     200  unit2
2   U100       US      0       0  unit3
3   U200       US     20     150  unit1
4   U200       US     25     200  unit2
5   U200       US      0       0  unit3
6   E100       EU     15     100  unit1
7   E100       EU     30     150  unit2
8   E100       EU      0       0  unit3
9   E200       EU     10     200  unit1
10  E200       EU     20     300  unit2
11  E200       EU     20     500  unit3
12  E300       EU     10     150  unit1
13  E300       EU     10     300  unit2
14  E300       EU     20     500  unit3
15  A100     Asia     10     150  unit1
16  A100     Asia     10     200  unit2
17  A100     Asia     20     500  unit3
18  A200     Asia     20     100  unit1
19  A200     Asia     10     150  unit2
20  A200     Asia     20     500  unit3
21  A300     Asia     20     200  unit1
22  A300     Asia     10     225  unit2
23  A300     Asia     20     500  unit3
24  A400     Asia     25     200  unit1
25  A400     Asia     20     225  unit2
26  A400     Asia     20     500  unit3
27  A500     Asia     25     200  unit1
28  A500     Asia     20     250  unit2
29  A500     Asia     20     500  unit3

0

这里有一个解决方案,它利用了数据框实际上具有隐藏的MultiIndex列的事实。具体来说,您的unit1_priceunit_vol等可以是一个具有级别unitprice的MultiIndex,例如pd.MultiIndex.from_tuples([('unit1', 'price'), ('unit1', 'vol'), ('unit2', 'price'), ('unit2', 'vol'), ('unit3', 'price'), ('unit3', 'vol')], names=['unit', 'measure'])。首先,您需要将uidlocation列移动到索引中。有了这个结构,DataFrame.stack就能做到您想要的。

import pandas as pd
df = pd.DataFrame(
    {
        'uid': ['U100', 'U200', 'E100', 'E200', 'E300', 'A100', 'A200', 'A300', 'A400', 'A500'],
        'location': ['US', 'US', 'EU', 'EU', 'EU', 'Asia', 'Asia', 'Asia', 'Asia', 'Asia'],
        'unit1_price': [10, 20, 15, 10, 10, 10, 20, 20, 25, 25],
        'unit1_vol': [100, 150, 100, 200, 150, 150, 100, 200, 200, 200],
        'unit2_price': [10, 25, 30, 20, 10, 10, 10, 10, 20, 20],
        'unit2_vol': [200, 200, 150, 300, 300, 200, 150, 225, 225, 250],
        'unit3_price': [0, 0, 0, 20, 20, 20, 20, 20, 20, 20],
        'unit3_vol': [0, 0, 0, 500, 500, 500, 500, 500, 500, 500]
    }
)
df.set_index(["uid", "location"], inplace=True)
col_idx = pd.MultiIndex.from_tuples(
    [c.split("_") for c in df.columns],
    names=["unit", "measure"]
)
df.columns = col_idx
final_df = df.stack(level=0).reset_index()
print(final_df)

结果是

measure   uid location   unit  price  vol
0        U100       US  unit1     10  100
1        U100       US  unit2     10  200
2        U100       US  unit3      0    0
3        U200       US  unit1     20  150
4        U200       US  unit2     25  200
5        U200       US  unit3      0    0
6        E100       EU  unit1     15  100
7        E100       EU  unit2     30  150
8        E100       EU  unit3      0    0
9        E200       EU  unit1     10  200
10       E200       EU  unit2     20  300
11       E200       EU  unit3     20  500
12       E300       EU  unit1     10  150
13       E300       EU  unit2     10  300
14       E300       EU  unit3     20  500
15       A100     Asia  unit1     10  150
16       A100     Asia  unit2     10  200
17       A100     Asia  unit3     20  500
18       A200     Asia  unit1     20  100
19       A200     Asia  unit2     10  150
20       A200     Asia  unit3     20  500
21       A300     Asia  unit1     20  200
22       A300     Asia  unit2     10  225
23       A300     Asia  unit3     20  500
24       A400     Asia  unit1     25  200
25       A400     Asia  unit2     20  225
26       A400     Asia  unit3     20  500
27       A500     Asia  unit1     25  200
28       A500     Asia  unit2     20  250
29       A500     Asia  unit3     20  500

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接