How to use pd.melt to unpivot a dataframe which columns share a prefix?

问题描述 投票:0回答:3

我正在尝试使用

pd.melt
取消透视我的数据,但到目前为止没有成功。每一行都是一家企业,数据包含有关该企业的信息和多条评论。我希望我的数据能够连续进行每一次评论。

我的前 150 个列以 15 个为一组,每个组列名称共享相同的模式

reviews/n/
for
0 < n < 9
。 (
reviews/0/text
reviews/0/date
,...,
reviews/9/date
)。 数据框中接下来的 65 列包含更多关于业务的数据(例如
business_id
address
),这些数据应保留为 id_variables。

我目前的数据是这样的:

business_id 地址 评论/0/日期 评论/0/文本 评论/1/日期 评论/1/文本
12345 01街 1/1/1990 “abc” 2/2/1995 “定义”

我的新数据框应该将每条评论都排成一行,而不是每条业务,看起来像这样:

business_id 地址 review_number review_date review_text
12345 01街 0 1/1/1990 “abc”
12345 01街 1 2/2/1995 “定义”

我尝试使用

pd.melt
但未能成功地编写出对我有价值的代码。

python pandas regex unpivot melt
3个回答
3
投票

你可以使用

pandas.wide_to_long()
做你想做的事。

但是,您首先需要将列从模式

reviews/N/COL
重命名为
reviews/COL/N
(或类似名称),因为
wide_to_long()
只能根据前缀逆透视,而在您的列名称中,您有一个前缀和一个后缀.

您可以手动执行此操作,例如使用

re
模块和适当的正则表达式:

df = df.rename(columns=lambda x: re.sub('reviews/(\d)/(.*)', r'review_\2\1', x))

在那之后,你的数据应该是这样的(注意改变的colnames):

business_id 地址 review_date0 review_text0 review_date1 review_text1
12345 01街 1/1/1990 abc 2/2/1995 def

现在您可以使用

pandas.wide_to_long()
并使用
stubnames
参数指定当您反透视时应分组的列的前缀。

df = pd.wide_to_long(df,
                     stubnames=['review_date','review_text'],
                     i=['business_id', 'address'], 
                     j='review_number')

最后,调用

.reset_index()
来达到你要求的结果。

完整示例:

import re
import pandas as pd

df = pd.DataFrame({'business_id': 12345, 
                   'address': '01 street', 
                   'reviews/0/date': '1/1/1990', 
                   'reviews/0/text': 'abc', 
                   'reviews/1/date': '2/2/1995', 
                   'reviews/1/text': 'def'}, index = [0])

df = df.rename(columns=lambda x: re.sub('reviews/(\d)/(.*)', r'review_\2\1', x))

df = pd.wide_to_long(df,
                     stubnames=['review_date','review_text'],
                     i=['business_id', 'address'], 
                     j='review_number').reset_index()

结果:

business_id 地址 review_number review_date review_text
12345 01街 0 1/1/1990 abc
12345 01街 1 2/2/1995 def

0
投票

可以得到所有非评论栏目的名称

columns = df.columns[~df.columns.str.match(r'reviews/\d+/')]
>>> columns
Index(['address', 'business_id'], dtype='object')

然后用它们

.melt()

df = df.melt(columns)

df['review_number'] = df['variable'].str.extract(r'reviews/(\d+)')
df['variable'] = df['variable'].str.replace(r'reviews/\d+/', 'review_')
>>> df
  address  business_id     variable                value review_number
0  street            1  review_date  1990-01-01 00:00:00             0
1  street            1  review_text                "abc"             0
2  street            1  review_date  1995-02-02 00:00:00             1
3  street            1  review_text                "def"             1

从那里你可以

.pivot()

>>> df.pivot(index=columns.union(['review_number']).to_list(), columns='variable')
                                                 value            
variable                                   review_date review_text
address business_id review_number                                 
street  1           0              1990-01-01 00:00:00       "abc"
                    1              1995-02-02 00:00:00       "def"

0
投票

一个选项是 pyjanitorpivot_longer - 在这种情况下,我们使用特殊占位符

.value
来标识我们希望保留为标题的列部分,而其余部分则整理到新列中:

(df
.pivot_longer(
    index = ['business_id', 'address'], 
    names_to = ['.value', 'reviewnumber', '.value'], 
    names_pattern = r"(review)s/(\d+)/(.+)"
 )
.rename(columns = lambda f: f.replace('review', 'review_'))
) 
   business_id    address review_number review_date review_text
0        12345  01 street             0    1/1/1990         abc
1        12345  01 street             1    2/2/1995         def

正则表达式可以灵活地将标签提取到单独的列中。请注意,您可以根据需要多次使用

.value
,只要您正确使用正则表达式即可。

© www.soinside.com 2019 - 2024. All rights reserved.