数据清洗2
一、置换和随机采样(permutation,sample)
-
随机置换(打乱排序(洗牌))函数:
numpy.random.permutation
,可以对一个序列进行随机排序,常用于数据集随机划分等场景np.random.permutation(x)
:如果x是一个整数n,它会返回一个打乱的0到n-1的整数数组(可以看成一个打乱的位置索引)
如果x是一个数组,它会返回一个打乱顺序后的新数组
print(np.random.permutation(5))
# 输出 [1 2 0 3 4]arr = np.array(['cat', 'dog', 'bird', "fish"])
print(np.random.permutation(arr))
# 输出: ['bird' 'fish' 'cat' 'dog']df = pd.DataFrame(np.arange(5*7).reshape((5,7)))
sampler = np.random.permutation(5)
df.take(sampler) # take()函数默认是行索引,因此再此处就是将df的行索引顺序设置成为打乱后的行索引
sampler = np.random.permutation(7)
df.take(sampler,axis="columns") # 对列索引进行打乱
- 随机采样函数:
sample()
- 默认随机抽取是无放回的抽样(抽样结果中一定没有重复的抽样数据),将replace 设置为True则会变成有放回的抽样(抽样结果中可能有重复的数据)
- weight: 设置每行/每列被取到的权重比
# 无放回抽样 (默认)
sample = df.sample(n=3) # 抽取3行
sample = df.sample(frac=0.6) # 抽取60%的数据# 有放回抽样
sample_with_replacement = df.sample(n=10, replace=True) # 抽取10行,可能有重复# 根据权重列抽样
weights = [0.1, 0.2, 0.3, 0.2, 0.2]
weighted_sample = df.sample(n=2, weights=weights)
二、计算分类编码(get_dummies
)
get_dummies()
是Pandas中用于执行独热编码(One-Hot Encoding)的函数,它将分类变量/特征转换为机器学习算法更易处理的数值形式
- data: 要转换的数据(Series/DataFrame)
- prefix: 为生成的列添加前缀
- prefix_sep: 前缀分隔符(默认为"_")
- dummy_na: 是否创建NaN的指示列(默认为False)
- columns: 指定要转换的列(当输入是DataFrame时)
- drop_first: 是否删除第一个类别(避免多重共线性)
- dtype: 输出数据类型(默认为uint8)
import pandas as pd# 示例数据
df = pd.DataFrame({'颜色': ['红', '蓝', '绿', '蓝', '红'],'尺寸': ['大', '中', '小', '大', '中']
})
df
# 基本编码
pd.get_dummies(df) # 生成将df中每个列名中的唯一的数据作为列名,的独特编码的dataframe
pd.get_dummies(df["尺寸"]).replace({True:1, False:0}) # 生成将df["尺寸"]中的数据作为列名的独特编码的dataframe
pd.get_dummies(df, prefix=['col', 'size']) # 自定义前缀
# 设置nan列
df.loc[2, '颜色'] = None
pd.get_dummies(df, dummy_na=True)
三、pandas扩展数据类型(Int8Dtype 、BooleanDtype、StringDtype、category
)
pandas原本建立在Numpy功能之上,所以数据类型沿用的numpy,这有一些问题:
-
整数,布尔值的缺失值被当成浮点数,导致不易察觉的问题
-
含有大量字符串数据时,计算开销大,同时占用很多内存
-
日期数据类型、稀疏数据的处理问题
-
因此派生出pandas扩展数据类型:
- pd.Int8Dtype / pd.Int16Dtype 替代 int数据类型
- pd.BooleanDtype 替代 bool数据类型
- pd.StringDtype 代替 object 数据类型,可被认为数string处理
s = pd.Series([1,2,3,None]) # 缺失值显示NAN s.dtype # 显示float64 nan解析成小数,但本质是想要整数的数据类型 # 用pandas的数据类型创建它,dtype执行,series的数据类型 s = pd.Series([1,2,3,None], dtype=pd.Int64Dtype()) # 缺失值显示<NA>表示整数类型的缺失值 s.dtype # Int64Dtype() 代替int64,这种扩展的类型支持带有缺失值的整数类型s = pd.Series(["one", "two", None, "three"]) s.dtype # 显示object类型 s = pd.Series(["one", "two", None, "three"], dtype=pd.StringDtype()) s.dtype # 显示StringDtype类型,将所有数据优先认为str处理s = pd.Series([True, False, None]) s.dtype # dtype('O') 表示数据类型为对象。这通常意味着该列包含字符串或混合类型的数据。但想要的是bool类型,只是包含了一个缺失值而已 s = pd.Series([True, False, None], dtype=pd.BooleanDtype()) s.dtype # BooleanDtype,代替bool类型,支持由有缺失值的bool类型
- pd.CategoricalDtype(简写就是category) 分类数据类型,在groupby、sort、value_counts 等操作中比 object 类型更快。
- 其他的(暂时还没用到)
# 创建自定义的数据dataframe fruits = ["apple", "orange", "apple", "apple"] * 10 N = len(fruits) rng = np.random.default_rng(seed=100) df = pd.DataFrame({'fruit': fruits,'basket_id': np.arange(N),'count': rng.integers(3, 15, size=N),'weight': rng.uniform(0, 4, size=N)},columns=['basket_id', 'fruit', 'count', 'weight']) # 查看category 和普通的 object 类型数据的差异 print(df.info()) # 内存占用memory usage: 11.1+ KB df = df.astype(dtype={'fruit': 'category', 'weight': 'category'}) print(df.info()) # 内存占用memory usage: 5.8 KB df["fruit"] = df["fruit"].cat.add_categories(["other"]) # 动态增加fruit类别 print(df["fruit"]) df["fruit"].cat.remove_unused_categories() # 删除没用在数据表中的类别# 创建分类对象1:可以从其他python序列直接创建 pd.Categorical,传入的是类别值(未被分类的数据) my_categories = pd.Categorical(['foo', 'bar', 'baz', 'foo', 'bar']) my_categories# 创建分类对象2:已经获取了数据的分类编码 和 具体类别,可以用from_codes把数据的分类编码转成对应类别 categories = ["foo", "bar", "baz"] codes = [0, 1, 2, 0, 0, 1] my_cats_2 = pd.Categorical.from_codes(codes, categories) my_cats_2# 注意:默认分类没有顺序,from_codes通过ordered参数指定为True可以指定数学上的顺序 ordered_cat = pd.Categorical.from_codes(codes, categories, ordered=True) ordered_cat # foo<bar<baz,ordered=True 不是"允许比较",而是"声明这些类别有真实顺序关系,请按我定义的顺序处理比较和排序"。就像告诉Pandas:"这些不是随便的标签,它们是有实际意义的等级/大小 # 使用了ordered = True之后,原本的series就拥有了部分数字的功能,能够对其使用一些数学函数 my_cats_2.as_ordered() # 对无序的分类对象通过as_ordered排序 。相当于 pandas_categorical的实例.as_ordered()
%timeit (测量代码的执行时间) %prun(性能分析)或 %memit(内存测量)魔法命令使用
四、采用正则表达式处理字符串
正则表达式提供在文本中搜索和匹配字符串模式的灵活方式, 正则表达式常称作regex,是根据正则表达式语言编写的字符串python的re模块负责对字符串应用正则表达式re模块的函数可以分为三类:模式匹配: findall(返回所有匹配的子串列表), search(扫描整个字符串查找匹配,返回第一个符合条件的子串), match(只匹配出现在字符串开始位置的模式,开始不符合就直接返回none);替换: sub(替换值,原始值:替换匹配字符串), subn, 拆分: split; regex描述的是在文本中定位的模式
- 使用
re.compile()
将正则表达式字符串编译成一个正则表达式对象,可以用于匹配字符串# import re text = """Dave dave@google.com Steve steve@gmail.com Rob rob@gmail.com Ryan ryan@yahoo.com"""pattern = r"[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}" # 识别电子邮件地址的正则表达式 regex = re.compile(pattern, flags=re.IGNORECASE) # flags设置的参数表示正则表达式不区分大小写
为什么要用 re.compile?提高效率:如果同一个正则表达式需要多次使用,编译后可以避免重复解析正则表达式字符串。代码更清晰:编译后的对象可以复用,使代码更模块化。支持更多操作:编译后的对象可以直接调用 match()、search()、findall() 等方法。
五、字符串操作
- python字符串对象的内置方法能轻松完成大部分文本运算, 对于更复杂的模式匹配和文本操作,则可能需要用到正则表达式
- pandas基于python的字符串方法,做了加强,能够对整组数据应用字符串方法和正则表达式,处理缺失数据
1、字符串分割与拼接
str.split(sep, n=-1, expand=False)
: 按指定分隔符(sep
)分割字符串,n
控制分割次数,expand=True
返回 DataFrame。str.rsplit(sep, n=-1, expand=False)
: 从右侧开始分割字符串。str.join(iterable)
: 用指定字符串连接序列中的元素。str.cat(others=None, sep=None, na_rep=None)
: 拼接字符串,支持与其他 Series 或列表拼接。df["Name"].str.split(",", expand=True) # 分割成多列 df["Words"].str.join("-") # 将列表中的字符串用 "-" 连接 df["First"].str.cat(df["Last"], sep=" ") # 合并两列
2、字符串匹配与提取
-
str.contains(pat, case=True, regex=True)
: 检查字符串是否包含指定模式(支持正则表达式)。 -
str.match(pat, case=True)
: 检查字符串是否以指定模式开头(正则表达式)。 -
str.find(sub)
/str.rfind(sub)
: 返回子字符串的首次/最后一次出现位置(未找到返回-1
)。df["fruit"].str.contains("apple|orange", case=False) # 不区分大小写 df["fruit"].str.match("a") # 是否以a字母开头 df["fruit"].str.find("p") # apple中返回1 df["fruit"].str.rfind("p") # apple中返回2
3、字符串替换
-
str.replace(pat, repl, regex=True)
: 替换字符串中的模式(支持正则表达式)。 -
str.strip(to_strip=None)
/str.lstrip()
/str.rstrip()
: 去除字符串两端的空白字符(或指定字符)。 -
str.translate(table)
: 接受一个字典作为参数,字典的键是要替代的字符的Unicode码,值是相应的映射字Unicode码。df["Phone"].str.replace(r'\D', '', regex=True) # 移除非数字字符ord("a") # 97chr(98) # 'b'df["fruit"].str.translate({97: 98}) # 将字符串中的a转化成b
4、字符串格式化与填充
-
str.lower()
/str.upper()
/str.title()
/str.capitalize()
: 转换大小写 -
str.pad(width, side='left', fillchar=' ')
: 填充字符串到指定长度。df["Name"].str.title() # 首字母大写 df["ID"].str.pad(5, side="left", fillchar="0") # 左填充 0
-
str.zfill(width)
: 用0
左填充字符串(类似pad
但固定填充0
)。
5、字符串长度与索引
-
str.len()
: 返回字符串长度。 -
str.get(i)
: 获取字符串的第i
个字符(类似 Python 的索引)。 -
str.slice(start, stop, step)
: 切片操作(类似 Python 的字符串切片)。df["Text"].str.len() # 计算每个字符串的长度 df["Code"].str.get(0) # 获取第一个字符 df["Date"].str.slice(0, 4) # 提取前 4 个字符(年份)
6、字符串判断
-
str.isalpha()
/str.isnumeric()
/str.isalnum()
: 检查字符串是否全为字母/数字/字母或数字。 -
str.startswith(pat)
/str.endswith(pat)
: 检查字符串是否以指定模式开头或结尾。df["URL"].str.endswith(".com") df["Password"].str.isalnum() # 是否只含字母和数字
7、其他实用方法
-
str.wrap(width, **kwargs)
: 自动换行(按指定宽度分割字符串)。 -
str.repeat(n)
: 重复字符串n
次。 -
str.count(pat)
: 统计子字符串出现的次数。df["Sentence"].str.count("the") # 统计 "the" 出现次数 df["Symbol"].str.repeat(3) # 如 "A" → "AAA"
总结
- 数据清洗,准备工作做的好可以让数据分析过程更顺畅,提高生产力
- 数据清洗: 缺失值处理, 重复值删除,数据转换和替换,检测和过滤异常值
- 数据准备: 重命名轴索引,分箱, 数据重排和采样,pandas扩展类型, 分类类型,字符串方法