我正在尝试使用
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
但未能成功地编写出对我有价值的代码。
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 |
可以得到所有非评论栏目的名称
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"
一个选项是 pyjanitor 的 pivot_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
,只要您正确使用正则表达式即可。