如何转置数据帧以计算值是不是存在的标志?
Posted
技术标签:
【中文标题】如何转置数据帧以计算值是不是存在的标志?【英文标题】:How to transpose dataframe to calculate flag of whether value exists or not?如何转置数据帧以计算值是否存在的标志? 【发布时间】:2017-11-30 08:10:48 【问题描述】:我有一个看起来像这样的数据名
Sno|UserID|TypeExp
1|JAS123|MOVIE
2|ASP123|GAMES
3|JAS123|CLOTHING
4|DPS123|MOVIE
5|DPS123|CLOTHING
6|ASP123|MEDICAL
7|JAS123|OTH
8|POQ133|MEDICAL
.......
10000|DPS123|OTH
UserID 是标识用户的列,TypeExp 列定义了该月的支出类型,现在我有大约 5 种不同的支出可能,即
TypeExpList=[电影、游戏、服装、医疗、其他]
现在我想将其转换为用户级别的数据帧,其中有一个 0 或 1 二进制变量存储信息天气或用户“X”是否完成了上述支出类型
例如在上面的快照数据帧输出应该看起来像
User| TypeExpList #Type list is this array corresponding entry's [MOVIE,GAMES,CLOTHING,MEDICAL,OTH]
JAS123 |[1,0,1,0,1] #since user has done expenditure on Movie,CLOTHING,OTHER Category
ASP123 |[0,1,0,1,0] #since User expenditure on GAMES & MEDICAL
DPS123 |[1,0,1,0,1] #since user expenditure on MOVIE,CLOTHING & OTHER
POQ133 |[0,0,0,1,0] #since User Expenditure on MEDICAL only
【问题讨论】:
【参考方案1】:这是你的输入数据集。
$ cat input.csv
Sno|UserID|TypeExp
1|JAS123|MOVIE
2|ASP123|GAMES
3|JAS123|CLOTHING
4|DPS123|MOVIE
5|DPS123|CLOTHING
6|ASP123|MEDICAL
7|JAS123|OTH
8|POQ133|MEDICAL
有了这个,你在UserID
上做pivot
而不是groupBy
。
val bins = spark
.read
.option("sep", "|")
.option("header", true)
.csv("input.csv")
.groupBy("UserID")
.pivot("TypeExp")
.count
.na
.fill(0)
scala> bins.show
+------+--------+-----+-------+-----+---+
|UserID|CLOTHING|GAMES|MEDICAL|MOVIE|OTH|
+------+--------+-----+-------+-----+---+
|POQ133| 0| 0| 1| 0| 0|
|JAS123| 1| 0| 0| 1| 1|
|DPS123| 1| 0| 0| 1| 0|
|ASP123| 0| 1| 1| 0| 0|
+------+--------+-----+-------+-----+---+
你有你的0
s 和1
s。最后一个技巧是在列上使用array
来构建正确的输出,其中位置表示支出。
val solution = bins.select(
$"UserID" as "User",
array("MOVIE","GAMES","CLOTHING","MEDICAL","OTH") as "TypeExpList")
scala> solution.show
+------+---------------+
| User| TypeExpList|
+------+---------------+
|POQ133|[0, 0, 0, 1, 0]|
|JAS123|[1, 0, 1, 0, 1]|
|DPS123|[1, 0, 1, 0, 0]|
|ASP123|[0, 1, 0, 1, 0]|
+------+---------------+
鉴于支出可能发生零次、一次或多次,count
超出支出可能会给出0
、1
或更高的数字。
您可以使用 UDF 对值进行二值化,并确保仅使用 0
s 和 1
s。
val binarizer = udf count: Long => if (count > 0) 1 else 0
val binaryCols = bins
.columns
.filterNot(_ == "UserID")
.map(col)
.map(c => binarizer(c) as c.toString)
val selectCols = ($"UserID" as "User") +: binaryCols
val solution = bins
.select(selectCols: _*)
.select(
$"User",
array("MOVIE","GAMES","CLOTHING","MEDICAL","OTH") as "TypeExpList")
scala> solution.show
+------+---------------+
| User| TypeExpList|
+------+---------------+
|POQ133|[0, 0, 0, 1, 0]|
|JAS123|[1, 0, 1, 0, 1]|
|DPS123|[1, 0, 1, 0, 0]|
|ASP123|[0, 1, 0, 1, 0]|
+------+---------------+
【讨论】:
【参考方案2】:crosstab
将完成大部分工作:
val table = df.stat.crosstab("UserID", "TypeExp")
+--------------+--------+-----+-------+-----+---+
|UserID_TypeExp|CLOTHING|GAMES|MEDICAL|MOVIE|OTH|
+--------------+--------+-----+-------+-----+---+
| ASP123| 0| 1| 1| 0| 0|
| DPS123| 1| 0| 0| 1| 0|
| JAS123| 1| 0| 0| 1| 1|
| POQ133| 0| 0| 1| 0| 0|
+--------------+--------+-----+-------+-----+---+
并且可以很好地与强类型 API 耦合:
table.map(_.toSeq match
case Seq(id: String, cnts @ _*) =>
(id, cnts.map(c => if(c != 0) 1 else 0))).toDF("UserId", "TypeExp")
+------+---------------+
|UserId| TypeExp|
+------+---------------+
|ASP123|[0, 1, 1, 0, 0]|
|DPS123|[1, 0, 0, 1, 0]|
|JAS123|[1, 0, 0, 1, 1]|
|POQ133|[0, 0, 1, 0, 0]|
+------+---------------+
【讨论】:
【参考方案3】:解决方案在 Scala 中,但在 PySpark 中也应该有点相似,因为它使用 DSL。 Spark 1.6+ 提供 Pivot
val pivotDf = df.groupBy($"userid").pivot("typeexp").agg(count($"typeexp") )
pivotDf.show
+------+--------+-----+-------+-----+---+
|userid|CLOTHING|GAMES|MEDICAL|MOVIE|OTH|
+------+--------+-----+-------+-----+---+
|DPS123| 1| 0| 0| 1| 0|
|JAS123| 1| 0| 0| 1| 1|
|ASP123| 0| 1| 1| 0| 0|
|POQ133| 0| 0| 1| 0| 0|
+------+--------+-----+-------+-----+---+
pivotDf.selectExpr("userid", "array(movie, games, clothing, medical,oth) as TypExpList")
.show
+------+---------------+
|userid| TypExpList|
+------+---------------+
|DPS123|[1, 0, 1, 0, 0]|
|JAS123|[1, 0, 1, 0, 1]|
|ASP123|[0, 1, 0, 1, 0]|
|POQ133|[0, 0, 0, 1, 0]|
+------+---------------+
【讨论】:
以上是关于如何转置数据帧以计算值是不是存在的标志?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 fstream::open() 检查 C++ 中是不是存在文件