读取数据
df = pd.read_csv('.csv')
输出形状和信息
print(df.shape)
print(df.info())
选择数字列和非数字列
df_numeric = df.select_dtypes(include=[np.number])
df_non_numeric = df.select_dtypes(exclude=[np.number])
cols = df.columns[:30] # first 30 columns
colours = ['#000099', '#ffff00'] # specify the colours - yellow is missing. blue is not missing.
sns.heatmap(df[cols].isnull(), cmap=sns.color_palette(colours))
missing_value = df.isnull().sum()
missing_value = missing_value[missing_value > 0]
missing_value_rate = missing_value/len(df)
missing_value_rate.sort_values(ascending=False)
# first create missing indicator for features with missing data
for col in df.columns:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0:
print('created missing indicator for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
# then based on the indicator, plot the histogram of missing values
ismissing_cols = [col for col in df.columns if 'ismissing' in col]
df['num_missing'] = df[ismissing_cols].sum(axis=1)
df['num_missing'].value_counts().reset_index().sort_values(by='index').plot.bar(x='index', y='num_missing')
删除样本(成行删除)
只有在我们确定缺失数据无法提供信息时,才可以执行该操作。否则,我们应当考虑其他解决方案。
# 删除了缺失值数量超过 35 的样本
# drop rows with a lot of missing values.
ind_missing = df[df['num_missing'] > 35].index
df_less_missing_rows = df.drop(ind_missing, axis=0)
我们只在确定某个特征无法提供有用信息时才丢弃它。
# 大于 47% 可以考虑删除
cols_to_drop = missing_value_rate[missing_value_rate > 0.47].index
print('number of features with more than 47% missing', len(cols_to_drop))
df_less_hos_beds_raion = df.drop(cols_to_drop, axis=1)
当特征是数值变量时,对同一特征的其他非缺失数据取平均值或中位数,用这个值来替换缺失值。
# 所有特征替换 中位数
# impute the missing values and create the missing value indicator variables for each numeric column.
df_numeric = df.select_dtypes(include=[np.number])
numeric_cols = df_numeric.columns.values
for col in numeric_cols:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0: # only do the imputation for the columns that have missing values.
print('imputing missing values for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
med = df[col].median()
df[col] = df[col].fillna(med)
单个特征填充
med = df['brent'].median() df['brent'] = df['brent'].fillna(med)
当特征是分类变量时,用众数(最频值)来填充缺失值。
# impute the missing values and create the missing value indicator variables for each non-numeric column.
df_non_numeric = df.select_dtypes(exclude=[np.number])
non_numeric_cols = df_non_numeric.columns.values
for col in non_numeric_cols:
missing = df[col].isnull()
num_missing = np.sum(missing)
if num_missing > 0: # only do the imputation for the columns that have missing values.
print('imputing missing values for: {}'.format(col))
df['{}_ismissing'.format(col)] = missing
top = df[col].describe()['top'] # impute with the most frequent value.
df[col] = df[col].fillna(top)
可以保留缺失值,使之提供有价值的信息
对于数值特征,我们可以用特定值(如 - 999
)来替换缺失值
对于分类特征,我们可以添加新的带值类别,如 _MISSING_
# categorical
df['sub_area'] = df['sub_area'].fillna('_MISSING_')
# numeric
df['life_sq'] = df['life_sq'].fillna(-999)
根据特征的属性(数值或分类),使用不同的方法来研究其分布,进而检测异常值。
df['life_sq'].hist(bins=100)
df.boxplot(column=['life_sq'])
对于数值特征,当异常值过于独特时,箱形图无法显示该值。因此,我们可以查看其描述统计学。
df['life_sq'].describe()
df['ecology'].value_counts().plot.bar()
其他方法:还有很多方法可以找出异常值,如散点图、z 分数和聚类
处理异常值的方法与处理缺失值有些类似:要么丢弃,要么修改,要么保留。
输入到模型中的所有数据应服务于项目目标。不必要数据即无法增加价值的数据。
有时一个特征不提供信息,是因为它拥有太多具备相同值的行。
为具备高比例相同值的特征创建一个列表
# 95% 的行是相同值的特征
num_rows = len(df.index)
low_information_cols = []
for col in df.columns:
cnts = df[col].value_counts(dropna=False)
top_pct = (cnts/num_rows).iloc[0]
if top_pct > 0.95:
low_information_cols.append(col)
print('{0}: {1:.5f}%'.format(col, top_pct*100))
print(cnts)
print()
我们需要了解重复特征背后的原因。当它们的确无法提供有用信息时,我们就可以丢弃它。
数据需要为项目提供有价值的信息。如果特征与项目试图解决的问题无关,则这些特征是不相关数据。
浏览特征,找出不相关的数据,当这些特征无法服务于项目目标时,删除之。
样本值完全重复
这种复制发生在观察值内所有特征的值均相同的情况下,很容易找出。
df_dedupped = df.drop('id', axis=1).drop_duplicates()
# there were duplicate rows
print(df.shape)
print(df_dedupped.shape)
删除基于一组唯一标识符的复制数据
我们可以设置一组关键特征作为唯一标识符,比如 timestamp、full_sq、life_sq、floor、build_year、num_room、price_doc。然后基于这些特征检查是否存在复制数据。
key = ['timestamp', 'full_sq', 'life_sq', 'floor', 'build_year', 'num_room', 'price_doc']
df.fillna(-999).groupby(key)['id'].count().sort_values(ascending=False).head(20)
处理方法:删除
key = ['timestamp', 'full_sq', 'life_sq', 'floor', 'build_year', 'num_room', 'price_doc']
df_dedupped2 = df.drop_duplicates(subset=key)
print(df.shape)
print(df_dedupped2.shape)
在类别值中混用大小写是一种常见的错误。这可能带来一些问题,因为 Python 分析对大小写很敏感。
df['sub_area'].value_counts(dropna=False) / df['sub_area'].unique()
# 将所有字母设置为小写(或大写)
df['sub_area_lower'] = df['sub_area'].str.lower()
df['sub_area_lower'].value_counts(dropna=False)
我们需要执行的另一个标准化是数据格式。比如将特征从字符串格式转换为 DateTime 格式
# 2020-05-12
df['timestamp_dt'] = pd.to_datetime(df['timestamp'], format='%Y-%m-%d')
df['year'] = df['timestamp_dt'].dt.year
df['month'] = df['timestamp_dt'].dt.month
df['weekday'] = df['timestamp_dt'].dt.weekday
print(df['year'].value_counts(dropna=False))
print()
print(df['month'].value_counts(dropna=False))
分类特征的值数量有限。有时由于拼写错误等原因可能出现其他值。
由于本文使用的房地产数据集不存在这类问题,因此我们创建了一个新的数据集。例如,city 的值被错误输入为「torontoo」和「tronto」,其实二者均表示「toronto」(正确值)。
识别它们的一种简单方式是模糊逻辑(或编辑距离)。该方法可以衡量使一个值匹配另一个值需要更改的字母数量(距离)。
可以规定所有值与「toronto」的距离在 2 个字母以内的转换为正确值。
参考资料
数据缺失、混乱、重复怎么办?最全数据清洗指南!