spark中DataFrame的使用方法

2020/07/08 -

引言

《Learning Spark》中使用的spark版本还是比较低的,所以对于DataFrame部分基本上没有涉及,虽然在sparkSql中提到了schemaRDD这个内容。到目前为止,我感觉DataFrame的很多使用方法是跟pandas的DataFrame很像的;

如果想看比较全面的操作,可以看这个文章,Spark-SQL之DataFrame操作大全 - 一片黑 - 博客园。本篇文章主要记录自己在平时使用中的一些问题。

注意:

  • 两个DF合并的时候,一定保证结构是一致的,即使是两个df结构一致,但是如果顺序不一致,也是不可行的。
  1. 获取DataFrame
    ------获取df的schema
  2. DataFrame删除列
  3. 按照条件筛选某些列
  4. 对某列进行值计数
  5. 新增一列,里面的值为常量
  6.  

具体使用方法

1. 获取DataFrame

目前使用过的获取DataFrame的形式有两种,一种就是直接采用底层的读写文件的方式直接获取,这种方式spark会帮助你推导出具体的数据类型,例如csv,json文件,但是如果csv文件没有具体的头部信息,估计就没有变量名称了;另一种方式是使用createDataFrame方法

##spark是一个sparkSession实例
test_df = spark.createDataFrame(some_rdd)

这种方法需要自己将具体的表头传递进去,关于数据的变量类型也可以传递进去。
之前看到过一个问题,我直接将json的rdd传递进去,一些层级结构的内容他是推导不出来的,这就比较尴尬;之后我是通过将某些字段挑出来,然后map一个新的rdd,这个rdd的每行是一个元组,然后只需要将表头传递进去就好了, 他会自动推导这个数据类型。

获取推导出来的数据类型方式有两种,df.schema,这种方式非常不直观,推荐使用df.printSchema()

2. DataFrame删除列

df.drop
帮助文档写的很简单,而且给出了一些示例,但是没有多列的示例;多列也比较简单;将多个名字传递进去就好了,不用跟pandas一样用数组的形式。

3. 按照条件筛选某些列

可以使用的方法是df.filterdf.where,实际上df.where就是df.filter的别名。
有两种方式可以选定某个列,df['xxx'],df.xxx,这两种方式都可以选定这个列。在筛选过程中可以直接使用上面两种方法来选定某个列,然后按照某种条件来过滤。
下面分别来说明几种方法。

#假设有两个列,ip和端口
#单个条件使用
df.filter(df.ip == "1.1.1.1").show()
df.filter("ip == '1.1.1.1'").show()
df.filter(df['ip'] == '1.1.1.1').show()
#多个条件时使用
df.fiter((df.ip == "1.1.1.1") & (df.port == 23)).show()
#注意看上面这个括号,没有括号会报错,不知道是因为使用了字符串的还是别的,可以测试
df.filter("ip == '1.1.1.1' and port == 23").show()

上面的筛选都是比较简单的,直接就是等号大于号这种;对于一些复杂的,可能逻辑不复杂,比如子字符串,或者说数值是否在某个集合中,这种内容我记得是可以使用sql实现的。不过这里还是使用按列进行的方式,问答[3]就是来解决字符串是否包含子字符串的。不过,我看了这个函数,通过help(df['one_col_name'])可以看到这个函数名字。所以,如果要解决这种比较复杂的逻辑,只需要去找这个就好了。我目前使用了两种,子字符串contains,和isIn集合内容。
例如我想看这个字符串是否包含某个字母:df.col_name.contains(".")

  • 判断该列是否存在空值
    filter("some_col is null").show()

4. 对某列进行值计数

query_info_df.groupBy("dest_ip").count().show()
query_info_df.groupBy("dest_ip").count().orderBy("count",ascending = False).show()

下面的是按照降序排序的版本;同时如果要是想看全部列的去重各是多少个,命令如下:

from pyspark.sql.functions import col,countDistinct
query_dns_df.agg(*(countDistinct(col(c)).alias(c) for c in query_dns_df.columns)).show()

要理解这个命令也很简单,首先来看单个列的时候,

query_dns_df.agg(countDistinct(col("dest_ip")).alias("count")).show()

 

 
执行结果


这是单个请求的结果,前面的多个参数的版本,在序列前面加上星号,是将这些单独的聚合函数都作为参数传递进去。
注意看agg本身的参数形式。

 
agg参数


上面多个参数的请求结果如下:

 
多个参数

以上内容说的都是统计个数,但是如果是想知道里面都有多少种数值,那么就需要别的办法了。

s_df.select('k').distinct()

5.新增一个列

5.1 单纯增加新列

今天有一个需求,操作DF并增加一个列,在pandas的使用过程中,满足这样的需求就是直接df['xx']=1,这样的形式。
但是因为spark中不支持这样的赋值操作,然后只能想办法用别的方式。
问答[1]中使用的方法是withColumns()
例如我这里要增加一个字符串常量。
df.withColumns("col_name", lit("some string"))
如果要增加更为复杂的结构,可以看文章的说法。文章[2]中还提到了利用sql语言的方式,这种没有尝试过。

5.2 按照原有列增加新列,或直接修改原列

我这里的需求是,要对某个列的字符串内容进行修改,按照一定的模式筛选某种符合的,然后进行修改。一开始想的办法是通过withColumn,然后使用udf的方式,但是实践中发现我这个UDF不知道为什么不好使,这个有点尴尬。这个UDF后面还是需要掌握一下。
然后就按照需求的关键词进行搜索,找到了文章[4],其中适合我的方式就是regexp_replace
使用方法如下,比如我需要将列中某种类型的字符串进行修改,

#“some_xxx" -> "some_yyy"
df.withColumn(new_col_name, regexp_replace(col(col_name), "xxx", "yyy"))

注意,regexp_replace是支持正则替换的。

参考文章

[1]How to add a constant column in a Spark DataFrame?
[2]Spark SQL DataFrame新增一列的四种方法
[3]Filter spark DataFrame on string contains
[4]Spark DataFrame:提取某列并修改/ Column更新、替换

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值