采坑 - 字符串的 "" 与 pd.isnull()
主要是记录一个采坑的过程. 当字符串 的 " " 和 pandas 中的 " " , NaN不是一个概念 .
需求
一个小伙伴要用 pandas 来处理一个, 表格填充的的问题, 脱敏数据大致是这样的.
区域名称 | 门店 | 店组 |
---|---|---|
龙华新城大区 | 壹城中心店 | 壹城中心一组 |
益田大区 | 皇岗口岸店 | 皇岗口岸一组 |
双龙大区 | 龙城中央旗舰店 | AAA店 |
深西大区 | 德佑麒麟A组 | |
A宏畅房地产经纪有限责任公司 | A地产经纪有限责任公司门店 | |
宏畅房地产经纪有限责任公司 | B地产经纪有限责任公司门店 | |
东部 | 微地产瑞凯洪湖A店 | |
景恒房地产有限公司 | D景恒房地产有限公司门店 | |
龙华新城大区 | 丰润花园店 | 丰润花园一组 |
现在想取一个 门店 字段, 新增或者覆盖 都可以, 取的逻辑是: "获取 门店 字段的值, 如果是空值, 则选择 店组 字段的值.
咋一看, (不考虑性能, 只管实现哈), 这不简单嘛,都不想考虑用什么 apply, 遍历 DataFrame 的每一行, 然后判断就好了.
踩过过程
通常我是先测试下, 与其说是测试, 其实就是百度下api, 根本就记不住的, 要是没有搜索引擎, 写代码立刻就废掉了.
先来波测试
import pandas as pd
df = pd.DataFrame({
"a":[1,2,3,4,5,6],
"门店": ["甲店", "", "", "丙店", "", "丁店"],
"店组": ["DD", "", "AA", "", "丁店", ""]
})
print(df)
a 门店 店组
0 1 甲店 DD
1 2
2 3 AA
3 4 丙店
4 5 丁店
5 6 丁店
为了演示方便, 这里选择新增 "新门店"字段, 不覆盖原来的哦 (如果 "门店", 没有值, 就取 "店组" 的值)
for 行号, 行值 in df.iterrows():
df.loc[行号, "新门店"] = 行值['门店'] or 行值["店组"]
print(df)
a 门店 店组 新门店
0 1 甲店 DD 甲店
1 2
2 3 AA AA
3 4 丙店 丙店
4 5 丁店 丁店
5 6 丁店 丁店
测试完美通过, 然后就开始整了.
翻车1次
df = pd.read_excel("门店选择.xlsx")
# 新增 "新门店"字段 也行 如果 "门店", 没有值, 就取 "店组" 的值
for 行号, 行值 in df.iterrows():
df.loc[行号, "新门店"] = 行值['门店'] or 行值["店组"]
print(df)
区域名称 | 门店 | 店组 | 新门店 | |
---|---|---|---|---|
0 | 龙华新城大区 | 壹城中心店 | 壹城中心一组 | 壹城中心店 |
1 | 益田大区 | 皇岗口岸店 | 皇岗口岸一组 | 皇岗口岸店 |
2 | 双龙大区 | 龙城中央旗舰店 | AAA店 | 龙城中央旗舰店 |
3 | 深西大区 | NaN | 德佑麒麟A组 | NaN |
4 | A宏畅房地产经纪有限责任公司 | A地产经纪有限责任公司门店 | NaN | A地产经纪有限责任公司门店 |
5 | 深圳市宏畅房地产经纪有限责任公司 | NaN | B地产经纪有限责任公司门店 | NaN |
6 | 东部 | 微地产瑞凯洪湖A店 | NaN | 微地产瑞凯洪湖A店 |
7 | 深圳市景恒房地产有限公司 | NaN | D景恒房地产有限公司门店 | NaN |
8 | 龙华新城大区 | 丰润花园店 | 丰润花园一组 | 丰润花园店 |
新门店字段, 出大事了, 并没有匹配到空值, 但是我上面的测试是没有问题. 突然有点紧张, 这就是 所谓的 bug 呀.
又想吐槽下 关于 bug 了. 我认为什么是 bug 呢, 测试没有问题, 真正用的时候, 有问题了, 而且不太轻易发现. 但更多时候, 我发现小伙伴总是喜欢滥用这个词, 都是写代码还好, 都明白, 很多是功能没有实现, 就暂时敷衍下, 说是bug, 其实就是写完, 很多时候. 但, 更有趣的是, 跟我们对接的业务却总爱说 bug, 动不动就说是 bug, 一行代码都没参与过的人, 却整天说 bug, 就莫名想吐槽下, 也没有别的意思, 有点滑稽哈哈.
BUG 定位
初级判断, 就是, 明明是空值, 却被认为是 True 呗.
for 行号, 行值 in df.iterrows():
print(行号, 行值) if 行值["门店"] == "" else print("没匹到空值")
没匹到空值
没匹到空值
没匹到空值
没匹到空值
没匹到空值
没匹到空值
没匹到空值
没匹到空值
没匹到空值
卧槽, 真的是报警了, 明明有空值, 去哪了了呢???
for 行号, 行值 in df.iterrows():
# if 行值["门店"] == '': print(行号, 行值)
#if 行值["门店"] == None: print(行号, 行值)
# if 行值["门店"] == 'nan': print(行号, 行值)
if 行值["门店"] == 'NaN': print(行号, 行值)
可以看到, 字符串的 " ", 在 pandas 里面, " ", None, 'NaN', 'nan' 都是匹不上的.
临时解决
当时有有点蒙圈, 只想这, 立马先解决, 不匹了, 直接填充, 然后判断吧.
df = pd.read_excel("门店选择.xlsx")
# 将整个表的的 缺失值 都填充为 "缺失值"
df = df.fillna("缺失值")
for 行号, 行值 in df.iterrows():
df.loc[行号, "新门店"] = 行值["门店"] if (行值["门店"] != "缺失值") else 行值["店组"]
print(df)
区域名称 | 门店 | 店组 | 新门店 | |
---|---|---|---|---|
0 | 龙华新城大区 | 壹城中心店 | 壹城中心一组 | 壹城中心店 |
1 | 益田大区 | 皇岗口岸店 | 皇岗口岸一组 | 皇岗口岸店 |
2 | 双龙大区 | 龙城中央旗舰店 | AAA店 | 龙城中央旗舰店 |
3 | 深西大区 | 缺失值 | 德佑麒麟A组 | 德佑麒麟A组 |
4 | A宏畅房地产经纪有限责任公司 | A地产经纪有限责任公司门店 | 缺失值 | A地产经纪有限责任公司门店 |
5 | 深圳市宏畅房地产经纪有限责任公司 | 缺失值 | B地产经纪有限责任公司门店 | B地产经纪有限责任公司门店 |
6 | 东部 | 微地产瑞凯洪湖A店 | 缺失值 | 微地产瑞凯洪湖A店 |
7 | 深圳市景恒房地产有限公司 | 缺失值 | D景恒房地产有限公司门店 | D景恒房地产有限公司门店 |
8 | 龙华新城大区 | 丰润花园店 | 丰润花园一组 | 丰润花园店 |
可以, 算是临时解决了这个 BUG. 但这并非是一个好的方式. 原因还是在于 pandas 的 NaN 和 咱平常的 "" 缺失值不是类对象.
BUG 根源 - NaN 和 ""
查了下文档才出坑. 在 Pandas 中, 对于缺失值, 默认会以 NaN 这个 pandas 专属的标志来表示. 但它并不是一个 数字, 或字符串 或 None, 因此, 是 不能和字符串 "" 来比较的.
pandas 中, 只能用 pd.isnull(), .. 等几个相关的方法来判断 '空值' NaN
-
pd.isnull ( ) ; pd.notnull ( )
-
pd.isna ( ) ; pd.notna( )
关于缺失值处理, 有两个极为重要的方法: dropna(), fillna()
真正解决:
# 直接覆盖原有的 "门店" 也行 如果 "门店", 没有值, 就取 "店组" 的值
df = pd.read_excel("门店选择.xlsx")
for 行号, 行值 in df.iterrows():
df.loc[行号, "新门店"] = 行值['门店'] if pd.notnull(行值['门店']) else 行值["店组"]
最后呢, 再测试一下, 这下用 pd.isnull () 或者 pd.isna() 就能匹配上 空值了.
df = pd.read_excel("门店选择.xlsx")
for 行号, 行值 in df.iterrows():
print(行号) if pd.isnull(行值["门店"]) else print("没匹到空值")
没匹到空值
没匹到空值
没匹到空值
3
没匹到空值
5
没匹到空值
7
没匹到空值
就记录下这个采坑, 记住空判断, 在 pandas 中一定要**用 pd.isna() 或者 pd.isnull() **, 尤其在遍历行的时候, 经常习惯性用 " " 或, "NaN" 或 None 等来处理.
虽然这个问题小, 但也是经验的累积嘛, 所谓, 吃一堑, 长一智嘛, 一点点积累起来, 就是经验了.