在逛CSDN论坛时遇到这样一个问题:

下列代码中srcdf和desdf都是Pandas的DataFrame对象,需要将srcdf转换为desdf,也就是根据列中的值拓展新的列,关系数据库报表中常见的需求,请问用DataFrame要如何实现?

  1. print(srcdf)
  2. 姓名 性别 科目 分数
  3. 编号
  4. 0 刘玄德 男 语文 98
  5. 1 刘玄德 男 数学 60
  6. 2 刘玄德 男 体育 50
  7. 3 关云长 男 语文 60
  8. 4 关云长 男 数学 60
  9. 5 关云长 男 体育 100
  10. [6 rows x 4 columns]
  11. print(desdf)
  12. 姓名 性别 语文 数学 体育 平均分
  13. 编号
  14. 0 刘玄德 男 98 60 50 66.666667
  15. 1 关云长 男 60 60 100 73.333333
  16. [2 rows x 6 columns]

经过分析,发现实际是将那么分组,将科目展开,即《利用pandas进行数据分析》第七章 数据转换下的将‘长格式’转换为‘宽格式’ 问题。论坛里已经有一种解决办法了:

  1. In [148]: from pandas import Series,DataFrame
  2. ...: a=[['刘玄德','男','语文',98.],['刘玄德','男','体育',60.],['关云长','男','数学',60.],['关云长','男','语文',100.]]
  3. ...: af=DataFrame(a,columns=['name','sex','course','score'])
  4. In [149]: af
  5. Out[149]:
  6. name sex course score
  7. 0 刘玄德 男 语文 98
  8. 1 刘玄德 男 体育 60
  9. 2 关云长 男 数学 60
  10. 3 关云长 男 语文 100
  11. In [150]: af.set_index(['name','sex','course'],inplace='TRUE')
  12. In [151]: af
  13. Out[151]:
  14. score
  15. name sex course
  16. 刘玄德 男 语文 98
  17. 体育 60
  18. 关云长 男 数学 60
  19. 语文 100
  20. In [152]: t1=af.unstack(level=2)
  21. In [153]: t1
  22. Out[153]:
  23. score
  24. course 体育 数学 语文
  25. name sex
  26. 关云长 男 NaN 60 100
  27. 刘玄德 男 60 NaN 98
  28. In [154]: t2=t1.mean(axis=1,skipna=True)
  29. In [155]: t2
  30. Out[155]:
  31. name sex
  32. 关云长 男 80
  33. 刘玄德 男 79
  34. dtype: float64
  35. In [156]: t1['平均分']=t2
  36. In [157]: t1
  37. Out[157]:
  38. score 平均分
  39. course 体育 数学 语文
  40. name sex
  41. 关云长 男 NaN 60 100 80
  42. 刘玄德 男 60 NaN 98 79
  43. In [158]: t1.fillna(0)
  44. Out[158]:
  45. score 平均分
  46. course 体育 数学 语文
  47. name sex
  48. 关云长 男 0 60 100 80
  49. 刘玄德 男 60 0 98 79

首先使用set_index 重建索引,这个函数很厉害,实际上是做了分组(groupby)和重建索引的工作。然后用unstack将行转换成列,最后算平均数,然后组合到一起。这里关键用到set_index(),unstack()。默认情况下,unstack的操作就是最内层的(这里就是level=2),除了传统分级编号,也可以用名称对其unstack。如果数据在分组中找不到的话会引入NaN。

下面我尝试用pivot和pivot_table解这个问题:

  1. #解法2:
  2. In [126]: a=[['刘玄德','男','语文',98.],['刘玄德','男','体育',60.],['关云长','男','数学',60.],['关云长','男','语文',100.]]
  3. ...: af=DataFrame(a,columns=['name','sex','course','score'])
  4. In [127]: af2=af.pivot('name','course','score') #使用pviot
  5. In [128]: af2['avg']=af2.mean(axis=1)
  6. In [129]: af2.fillna(0)
  7. Out[129]:
  8. course 体育 数学 语文 avg
  9. name
  10. 关云长 0 60 100 80
  11. 刘玄德 60 0 98 79
  12. In [130]: af2
  13. Out[130]:
  14. course 体育 数学 语文 avg
  15. name
  16. 关云长 NaN 60 100 80
  17. 刘玄德 60 NaN 98 79
  18. In [131]: af2[af2.isnull()]=0
  19. In [132]: af2
  20. Out[132]:
  21. course 体育 数学 语文 avg
  22. name
  23. 关云长 0 60 100 80
  24. 刘玄德 60 0 98 79

pivot的前两个参数值分别作用于行和列索引,最后一个参数值则是用于填充DaraFrame的数据列的列名。在《利用pandas进行数据分析》第七章 数据转换下的将‘长格式’转换为‘宽格式’ 中作者一语道破了pivot和上面做法的区别:

数据集中性表格(数据行列转置数据透视)(1)

接下来我尝试用更简单的方法去得到上面的结果,在《利用pandas进行数据分析》书中,第九章 讲了透视表和交叉表。

pivot_table 就是数据透视表,用过EXCEL数据透视表的对此肯定很熟悉。不过目前函数的参数有所更新,原来的rows变成了index,cols变成了columns。

数据集中性表格(数据行列转置数据透视)(2)

  1. #解法3:
  2. af.pivot_table('score',index='name',columns='course',aggfunc='mean',margins=True,fill_value=0)
  3. Out[141]:
  4. course 体育 数学 语文 All
  5. name
  6. 关云长 0 60 100 80.0
  7. 刘玄德 60 0 98 79.0
  8. All 60 60 99 79.5

数据集中性表格(数据行列转置数据透视)(3)

,