DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)

文章目录

      • 使用场景
    • 行转列:unstack()
      • 1. 从数据库读表
      • 2. 依四列值分成4个dataframe,使用unstack()及进行行转列
      • 3. 四个DataFrame合并:join()
      • 4.处理列名:rename_axis()
      • 若3.4.两步调换顺序
        • 3_1.重置索引reset_index()
        • 4_2.四个DataFrame合并:merge()
    • 排序后去重(sort_values,drop_duplicates)

使用场景

各个含report_id的表在进行表间连接时要保证report_id 不重复,即不存在一对多的关系。因此要先消除一对多关系,一种方法就是行转列unstack(),另一种就是简单的去除重复值。

PS:在索引设置的步骤,发现若先对各个dataframe reset_index(),就只能用merge指定依’report_id’合并,不能使用join()。是因为join()默认以index作为对齐的列,索引重置后join报错:ValueError: columns overlap but no suffix specified: 列重叠但未指定后缀。
博客中介绍了两种方式的代码及结果。

待处理数据表说明:
【例表部分数据如图一,码住的字段不用管,每个report_id对应有5种subtype导致有5条数据,现要进行行转列。】
DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第1张图片

行转列:unstack()

pandas中的Series对象有一个方法叫做unstack,调用一个具有二级索引的Series对象的unstack方法,会得到一个DataFrame对象。其索引就是Series对象的一级索引,列就是Series对象的二级索引

下图是unstack()能实现的效果:
在这里插入图片描述
参考:https://www.cnblogs.com/traditional/p/11967360.html这篇对行转列、列转行写的十分详细。

1. 从数据库读表

product_db=pymysql.connect(host='**', port=**,user="**", password="**", database="**", charset='utf8' )
sql='''
select t.* 
from default_info t 
'''
df=pd.read_sql(sql,product_db)
df.head() # 默认查看前五行

DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第2张图片

2. 依四列值分成4个dataframe,使用unstack()及进行行转列

从图二的效果图可以看出只适用于三列的data,所以处理图一的四个数据列需要四次unstack,然后使用join或者merge合并。

以default_count这列为例:

unstack_count_df=df.set_index(['report_id','default_subtype'])['default_count'].unstack().add_prefix('default_subtype_').add_suffix('_count')
unstack_count_df.head()

代码拆分一下:

1、 将"report_id"和"default_subtype"设置为索引, 然后取出"default_count"这一列, 得到对应的具有二级索引的 Series 对象 :
df.set_index(['report_id','default_subtype'])['default_count']
DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第3张图片

PS:如何看取出一列数据后是什么类型 一般python使用type函数查看数据类型,但是会报错: 在这里插入图片描述 所以使用
isinstance() isinstance(df.set_index(['report_id','default_subtype'])['default_count'], pd.Series)

2、调用具有二级索引的Series的unstack, 会得到一个DataFrame 并会自动把一级索引变成DataFrame的索引, 二级索引变成DataFrame的列
unstack_count_df.unstack()
DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第4张图片

add_prefix('default_subtype_').add_suffix('_count')加前缀后缀方便连接。
DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第5张图片

3. 四个DataFrame合并:join()

merge_df=unstack_count_df.join(unstack_month_df).join(unstack_overdue_sum_df).join(unstack_overdue_mon_df)

4.处理列名:rename_axis()

这个DataFrame的索引和列都有一个名字
索引的名字叫"report_id", 列的名字叫"default_subtype", 因为原来Series的两个索引就叫"report_id"和"default_subtype"
可以通过 rename_axis(index=, columns=) 来给坐标轴重命名

#join()后列和行都有名字,修改列名,索引名不改(原来的索引有名字, 那么reset_index之后, 列名就是原来的索引名)
merge_df=merge_df.rename_axis(columns=None).reset_index()

这里详见:pandas行转列、列转行、以及一行生成多行

若3.4.两步调换顺序

3_1.重置索引reset_index()

Pandas reset_index()是一种重置数据帧索引的方法。
reset_index()方法将范围从0到数据长度的整数列表设置为索引。

用法:

DataFrame.reset_index(level=None, drop=False, inplace=False,
col_level=0, col_fill=”)

参数:
level:int,字符串或列表以选择并从索引中删除传递的列。 drop:布尔值,如果为False,则将替换的索引列添加到数据中。
inplace:布尔值,如果为True,则对原始 DataFrame 本身进行更改。 col_level:选择在哪个列级别插入标签。
col_fill:对象,以确定如何命名其他级别。

返回类型:
DataFrame

unstack_count_df.unstack().reset_index()

DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第6张图片

4_2.四个DataFrame合并:merge()

# 假设两个reset_index()之后的df1,df2
# 两个df依report_id连接,outer表示两个df的report_id取并集
out=df2.merge(df1,on='report_id',how='outer') 
out.head()

DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第7张图片

# 索引列的名字去掉就和前一种方法的输出相同了
df=out.rename_axis(columns=None)

DataFrame 去除一对多关系之行转列方法详解(附unstack()使用场景及代码)_第8张图片

  • merge 默认的合并方式:inner,交集
  • merge outer,并集,NaN补全
  • merge left,左边为准,NaN补全
  • merge right,右边为准,NaN补全

如果你的数据有重复,或是只有某些无关紧要的数据列不同,还有另一种方法去除一对多:

排序后去重(sort_values,drop_duplicates)

按照report_id和first_month排序,去重时选择保留第一个,就会report_id 相同的各行中保留日期最早的一条,达到去除一对多的目的。】

biz_df=biz_df.sort_values(by=['report_id','first_month'])
biz_df.drop_duplicates(['report_id'],keep='first',inplace=True) # 最早日期

排坑:
drop_duplicates()这里的inplace默认是false,设置为True就会在原dataframe上改,就不要写成df=df.drop_duplicates([‘report_id’],keep=‘first’,inplace=True)

会将df变成NoneType。

你可能感兴趣的:(Python初学,python,unstack,行转列)