介绍PySpark第二部分 - 选择、过滤和排序数据
这是一系列博文的第二部分,是对PySpark的介绍。
本系列博文的其他部分可以在这里找到。
在这一部分中,我将涵盖。
- 选择列
- 筛选行
- 连锁条件
- 字符串匹配
- 针对列表的匹配
- 非
- 对空值进行过滤
- 丢弃重复的数据
- 数据排序
选择列
select 我们可以使用DataFrame 的方法来选择我们感兴趣的列。
如果我们加载我们在上一节中看到的pokemon parquet文件,那么我们就可以只选择我们感兴趣的列,由于命令是被懒惰地评估的,当我们从parquet文件中加载时,我们最终只会加载我们感兴趣的列。
pokemon = spark.read.parquet('/mnt/tmp/pokemon.parquet')
pokemon = pokemon.select('#', 'Name', 'Type1', 'Type2', 'Generation')
display(pokemon.limit(10))
.table-result-container { max-height: 300px; overflow: auto; } table, th, td { border:1px solid black; border-collapse: collapse; } th, td { padding:5px; } th { text-align: left; }
| # | 名称 | 类型1 | 类型2 | 代数 |
|---|---|---|---|---|
| 1 | 布巴索尔 | 草系 | 毒药 | 1 |
| 2 | 伊维萨乌 | 草类 | 蛇毒 | 1 |
| 3 | 维努萨龙 | 草质 | 毒药 | 1 |
| 3 | 维努沙尔巨型维努沙尔 | 草质 | 毒药 | 1 |
| 4 | 魅魔 | 火焰 | 无 | 1 |
| 5 | 恶魔之眼(Charmeleon | 火灾 | 空 | 1 |
| 6 | 蜥蜴 | 火焰 | 飞翔 | 1 |
| 6 | 魔法师Mega Charizard X | 火焰 | 龙 | 1 |
| 6 | 火蜥蜴Mega Charizard Y | 火龙 | 飞翔 | 1 |
| 7 | 小松鼠 | 水系 | 无 | 1 |
我们还可以使用DataFrame的colRegex 方法来匹配列,这将给我们提供匹配正则表达式的列,让我们试试。
display(
pokemon.select(
pokemon.colRegex('`^T[a-z]*[1-9]$`')
).limit(5)
)
| 类型1 | 类型2 |
|---|---|
| 草类 | 毒药 |
| 草类 | 毒药 |
| 草类 | 毒草 |
| 草 | 毒草 |
| 火灾 | 空 |
过滤行
我们可以用类似于在pandas中的方式来过滤行,比如说我们只想要第二代小精灵。
second_gen_pokemon = pokemon[pokemon['Generation'] == 2]
display(second_gen_pokemon.limit(10))
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 152 | 奇科里塔 | 草 | 无 | 2 |
| 153 | 贝利夫 | 草地 | 空 | 2 |
| 154 | 牧羊犬 | 草地 | 无 | 2 |
| 155 | 骏达奎尔 | 火 | 空 | 2 |
| 156 | 奎拉瓦 | 火灾 | 空 | 2 |
| 157 | 大爆炸 | 火灾 | 空 | 2 |
| 158 | 托托迪尔 | 水 | 空 | 2 |
| 159 | 鳄鱼人 | 水 | 空 | 2 |
| 160 | 法拉里加特 | 饮用水 | 空 | 2 |
| 161 | 森特雷特 | 正常 | 空 | 2 |
连锁条件
我们可以使用& 或| 来链接条件,所以如果我们想选择所有"Ground" 类型的小精灵。
ground_type_pokemon = pokemon[
(pokemon['Type1'] == 'Ground')
| (pokemon['Type2'] == 'Ground')
]
display(ground_type_pokemon.limit(5))
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 27 | 沙虫 | 地面 | 无 | 1 |
| 28 | 泥石流(Sandslash | 地面 | 空 | 1 |
| 31 | 尼多奎恩 | 毒药 | 地面 | 1 |
| 34 | 尼多金 | 毒药 | 地面 | 1 |
| 50 | 挖掘机 | 地面 | 无 | 1 |
字符串匹配
我们可以使用startswith,endswith, 或contains 来过滤字符串。
display(pokemon[pokemon['Name'].startswith('Nido')])
.table-result-container { max-height: 300px; overflow: auto; } table, th, td { border:1px solid black; border-collapse: collapse; } th, td { padding:5px; } th { text-align: left; }
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 29 | 尼多兰![]() | 毒药 | 无 | 1 |
| 30 | 尼多里纳 | 麻醉剂 | 空 | 1 |
| 31 | 尼多奎恩 | 药品 | 地面 | 1 |
| 32 | 尼多兰![]() | 毒药 | 空 | 1 |
| 33 | 尼多里诺 | 麻醉剂 | 空 | 1 |
| 34 | 尼多金 | 毒药 | 地面 | 1 |
我们还可以使用like 和rlike ,其中rlike 用于在正则表达式上进行匹配 - 让我们寻找名字以 "R "开头、以 "n "结尾的小精灵。
display(
pokemon[ pokemon['Name'].rlike('^[Rr].+n$')
]
)
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 111 | 雷恩 | 地面 | 岩石 | 1 |
| 112 | 雷德恩 | 地面 | 岩石 | 1 |
针对列表进行匹配
我们可以根据一个列表中的值进行过滤,使用isin
display(
pokemon[ pokemon['#'].isin([121, 131, 141, 151])
]
)
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 121 | 斯塔米 | 水系 | 通灵 | 1 |
| 131 | 拉普拉斯 | 水系 | 冰 | 1 |
| 141 | 卡布托普斯 | 岩石 | 水 | 1 |
| 151 | 苗族 | 通灵 | 空 | 1 |
NOT (~)
我们可以通过使用a tilde~ 符号来获得上述任何过滤器的反面。
display(
pokemon[ ~(pokemon['Type1'].isin(['Grass', 'Fire', 'Water', 'Bug']))
& ~(pokemon['Generation'] == 1)
].limit(10)
)
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 161 | 哨兵 | 正常 | 无 | 2 |
| 162 | 芙蕾特 | 正常 | 无 | 2 |
| 163 | 虎头蛇尾 | 正常 | 飞翔 | 2 |
| 164 | 野鸟 | 普通 | 飞行 | 2 |
| 169 | 鹦鹉 | 毒药 | 鸓 オヤエオ | 2 |
| 172 | 皮库 | 电击 | 无 | 2 |
| 173 | 克利法 | 仙女 | 空 | 2 |
| 174 | 伊格利布夫 | 正常 | 仙女 | 2 |
| 175 | 托格皮 | 仙女 | 无 | 2 |
| 176 | 托吉迪 | 仙女 | 飞翔 | 2 |
对空值进行过滤
我们可以使用isNull ,对空值进行过滤,所以如果我们只想寻找具有单一类型的小精灵,我们可以这样做。
single_type_pokemon = pokemon[pokemon['Type2'].isNull()]
display(single_type_pokemon.limit(5))
| # | 名称 | 类型1 | 类型2 | 代号 |
|---|---|---|---|---|
| 4 | 魅力鸟 | 火 | 无 | 1 |
| 5 | 恶魔之眼(Charmeleon | 火灾 | 空 | 1 |
| 7 | 小松鼠 | 水系 | 空 | 1 |
| 8 | 瓦尔特尔 | 水 | 空 | 1 |
| 9 | 布拉斯托斯 | 水 | 空 | 1 |
或者反过来说,如果我们只想要有Type1和Type2的小精灵,我们就用我们的"~"。
dual_type_pokemon = pokemon[~pokemon['Type2'].isNull()]
display(dual_type_pokemon.limit(5))
| # | 名称 | 类型1 | 类型2 | 代数 |
|---|---|---|---|---|
| 1 | 布巴索尔 | 草系 | 毒药 | 1 |
| 2 | 伊维萨乌 | 草类 | 蛇毒 | 1 |
| 3 | 维努萨龙 | 草质 | 毒药 | 1 |
| 3 | 维努沙尔巨型维努沙尔 | 草 | 毒药 | 1 |
| 6 | 蜥蜴 | 火焰 | 飞行 | 1 |
丢掉重复的行
你可能已经注意到,有时我们有小精灵的编号(#)重复,因为我们有该小精灵的普通版本和 "巨型 "版本,我们可以使用DataFrame的dropDuplicates 方法删除重复的数字。如果我们想只删除任何重复的行,我们不需要提供一个列的子集,但通过提供一个列的子集,我们可以删除在任何单一/组合的列中有重复的行。
display(pokemon.dropDuplicates(subset=['#']).limit(10))
| # | 名称 | 类型1 | 类型2 | 代数 |
|---|---|---|---|---|
| 1 | 布巴索尔 | 草系 | 毒药 | 1 |
| 2 | 伊维萨乌 | 草类 | 蛇毒 | 1 |
| 3 | 维努萨龙 | 草质 | 毒药 | 1 |
| 4 | 魅魔 | 火焰 | 无 | 1 |
| 5 | 恶魔之眼(Charmeleon | 火灾 | 空 | 1 |
| 6 | 蜥蜴 | 火焰 | 飞翔 | 1 |
| 7 | 小松鼠 | 水系 | 无 | 1 |
| 8 | 瓦尔特尔 | 水 | 空 | 1 |
| 9 | 布拉斯托斯 | 水 | 空 | 1 |
| 10 | 卡特彼勒 | 虫子 | 空 | 1 |
注意,现在我们的数据中不再有MegaVenusaur或MegaCharizard。
数据排序
目前我们的DataFrame是按小精灵的编号排序的 (#),如果我们再次加载数据,我们可以选择用sort 或orderBy 来按其他属性排序。
pokemon = spark.read.parquet('/mnt/tmp/pokemon.parquet')
pokemon = pokemon.sort('HP', ascending=False)
display(pokemon.limit(10))
| # | 名称 | 类型1 | 类型2 | HP | 攻击力 | 防御 | 攻击力 | 防御 | 速度 | 寿命 | 传奇的 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 242 | 布莱西 | 普通 | 无 | 255 | 10 | 10 | 75 | 135.0 | 55 | 2 | 假的 |
| 113 | 昌西 | 正常 | 空 | 250 | 5 | 5 | 35 | 105.0 | 50 | 1 | 假的 |
| 202 | 沃布费特 | 精神病学 | 空 | 190 | 33 | 58 | 33 | 58.0 | 33 | 2 | 假的 |
| 321 | 纬度 | 水 | 空 | 170 | 90 | 45 | 90 | 45.0 | 60 | 3 | 假的 |
| 594 | 阿罗莫拉 | 水 | 空 | 165 | 75 | 80 | 40 | 45.0 | 65 | 5 | 假的 |
| 143 | 斯诺拉 | 正常 | 空 | 160 | 110 | 65 | 65 | 110.0 | 30 | 1 | 假的 |
| 289 | 荡气回肠 | 正常 | 空 | 150 | 160 | 100 | 95 | 65.0 | 100 | 3 | 假的 |
| 426 | 漂浮物 | 幽灵 | 鸓 オヤエオヤエオヤエオヤエオヤエオヤエ | 150 | 80 | 44 | 90 | 54.0 | 80 | 4 | 假的 |
| 487 | 吉拉蒂纳改变的形象 | 幽灵 | 龙 | 150 | 100 | 120 | 100 | 120.0 | 90 | 4 | 真 |
| 487 | 吉拉蒂纳原产地名称 | 幽灵 | 龙 | 150 | 120 | 100 | 120 | 100.0 | 90 | 4 | 真 |
有多个小精灵的HP为150,如果我们想先按"HP" ,后按"Attack" ,但按攻击力升序排列,我们可以这样做。
pokemon = pokemon.sort(['HP', 'Attack'], ascending=[0, 1])
display(pokemon.limit(10))
.table-result-container { max-height: 300px; overflow: auto; } table, th, td { border:1px solid black; border-collapse: collapse; } th, td { padding:5px; } th { text-align: left; }
| # | 名称 | 类型1 | 类型2 | HP | 攻击力 | 防御 | 攻击力 | 防御 | 速度 | 寿命 | 传奇的 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 242 | 布莱西 | 普通 | 无 | 255 | 10 | 10 | 75 | 135.0 | 55 | 2 | 假的 |
| 113 | 昌西 | 正常 | 空 | 250 | 5 | 5 | 35 | 105.0 | 50 | 1 | 假的 |
| 202 | 沃布费特 | 精神病学 | 空 | 190 | 33 | 58 | 33 | 58.0 | 33 | 2 | 假的 |
| 321 | 纬度 | 水 | 空 | 170 | 90 | 45 | 90 | 45.0 | 60 | 3 | 假的 |
| 594 | 阿罗莫拉 | 水 | 空 | 165 | 75 | 80 | 40 | 45.0 | 65 | 5 | 假的 |
| 143 | 斯诺拉 | 正常 | 空 | 160 | 110 | 65 | 65 | 110.0 | 30 | 1 | 假的 |
| 426 | 漂浮物 | 幽灵 | 鸓 オヤエオヤエオヤエオヤエオヤエオヤエ | 150 | 80 | 44 | 90 | 54.0 | 80 | 4 | 假的 |
| 487 | 吉拉蒂纳改变的形象 | 幽灵 | 龙 | 150 | 100 | 120 | 100 | 120.0 | 90 | 4 | 真 |
| 487 | 吉拉蒂纳原产地名称 | 幽灵 | 龙 | 150 | 120 | 100 | 120 | 100.0 | 90 | 4 | 真 |
| 289 | 荡气回肠 | 正常 | 空 | 150 | 160 | 100 | 95 | 65.0 | 100 | 3 | 错误 |

