25、自定义函数和类实现数据清洗自动化

自定义函数和类实现数据清洗自动化

在数据处理过程中,我们经常需要对数据进行清洗、检查异常值、聚合数据等操作。为了提高代码的复用性和效率,我们可以定义一些自定义函数和类来实现这些功能。本文将详细介绍如何创建和使用这些函数和类,以及它们在实际数据处理中的应用。

1. 检查变量分布和识别异常值

在处理数据时,了解变量的分布情况以及识别异常值是非常重要的。我们可以通过创建自定义函数来完成这些任务。

1.1 导入必要的库并加载数据

首先,我们需要导入一些必要的库,并加载相关的数据。以下是具体的代码:

import pandas as pd
import os
import sys
import pprint
nls97 = pd.read_csv("data/nls97f.csv")
nls97.set_index('personid', inplace=True)
covidtotals = pd.read_csv("data/covidtotals720.csv")
1.2 创建函数显示分布的重要属性

我们创建一个名为 getdistprops 的函数,该函数接受一个序列作为输入,并返回一个包含分布的中心趋势、形状和离散程度等指标的字典。以下是函数的代码:

import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as scistat
import math

def getdistprops(seriestotest):
    out = {}
    normstat, normpvalue = scistat.shapiro(seriestotest)
    if (not math.isnan(normstat)):
        out['normstat'] = normstat
        if (normpvalue>=0.05):
            out['normpvalue'] = str(round(normpvalue, 2)) + ": Accept Normal"
        elif (normpvalue<0.05):
            out['normpvalue'] = str(round(normpvalue, 2)) + ": Reject Normal"
    out['mean'] = seriestotest.mean()
    out['median'] = seriestotest.median()
    out['std'] = seriestotest.std()
    out['kurtosis'] = seriestotest.kurtosis()
    out['skew'] = seriestotest.skew()
    out['count'] = seriestotest.count()
    return out

将上述函数保存到 helperfunctions 子文件夹下的 outliers.py 文件中。

1.3 调用函数检查变量分布

我们将 covidtotals 数据集中的 total_cases_pm 序列传递给 getdistprops 函数,以检查该变量的分布情况。以下是调用代码:

import outliers as ol
dist = ol.getdistprops(covidtotals.total_cases_pm)
pprint.pprint(dist)

输出结果如下:

{'count': 209,
 'kurtosis': 26.137524276840452,
 'mean': 2297.0221435406693,
 'median': 868.866,
 'normpvalue': '0.0: Reject Normal',
 'normstat': 0.5617035627365112,
 'skew': 4.284484653881833,
 'std': 4039.840202653782}

从输出结果可以看出, total_cases_pm 变量的分布具有显著的正偏态和较厚的尾部,这与正态分布不同。

1.4 创建函数列出数据框中的异常值

我们创建一个名为 getoutliers 的函数,该函数用于列出数据框中的异常值。以下是函数的代码:

def getoutliers(dfin, sumvars, othervars):
    dfin = dfin[sumvars + othervars]
    dfout = pd.DataFrame(columns=dfin.columns, data=None)
    dfsums = dfin[sumvars]
    for col in dfsums.columns:
        thirdq, firstq = dfsums[col].quantile(0.75), dfsums[col].quantile(0.25)
        interquartilerange = 1.5*(thirdq-firstq)
        outlierhigh, outlierlow = interquartilerange+thirdq, firstq-interquartilerange
        df = dfin.loc[(dfin[col]>outlierhigh) | (dfin[col]<outlierlow)]
        df = df.assign(varname = col, threshlow = outlierlow, threshhigh = outlierhigh)
        dfout = pd.concat([dfout, df])
    return dfout
1.5 调用函数列出异常值

我们将 nls97 数据框传递给 getoutliers 函数,并指定要检查的列和要包含在结果中的列。以下是调用代码:

sumvars = ['satmath','wageincome']
othervars = ['originalid','highestdegree','gender','maritalstatus']
outliers = ol.getoutliers(nls97, sumvars, othervars)
outliers.varname.value_counts(sort=False)
outliers.loc[outliers.varname=='satmath', othervars + sumvars]
outliers.to_excel("views/nlsoutliers.xlsx")

输出结果显示了每个变量的异常值数量,以及 satmath 变量的异常值。

1.6 创建函数生成直方图和箱线图

我们创建一个名为 makeplot 的函数,该函数用于生成直方图和箱线图。以下是函数的代码:

def makeplot(seriestoplot, title, xlabel, plottype="hist"):
    if (plottype=="hist"):
        plt.hist(seriestoplot)
        plt.axvline(seriestoplot.mean(), color='red', linestyle='dashed', linewidth=1)
        plt.xlabel(xlabel)
        plt.ylabel("Frequency")
    elif (plottype=="box"):
        plt.boxplot(seriestoplot.dropna(), labels=[xlabel])
    plt.title(title)
    plt.show()
1.7 调用函数生成直方图和箱线图

我们将 nls97 数据框中的 satmath 列传递给 makeplot 函数,分别生成直方图和箱线图。以下是调用代码:

ol.makeplot(nls97.satmath, "Histogram of SAT Math", "SAT Math")
ol.makeplot(nls97.satmath, "Boxplot of SAT Math", "SAT Math", "box")
2. 数据聚合和合并

在数据处理过程中,我们经常需要对数据进行聚合和合并操作。我们可以通过创建自定义函数来完成这些任务。

2.1 导入必要的库

首先,我们需要导入一些必要的库。以下是具体的代码:

import pandas as pd
import os
import sys
2.2 创建函数按组和时间段聚合值

我们创建一个名为 adjmeans 的函数,该函数用于按组和时间段聚合值。以下是函数的代码:

def adjmeans(df, byvar, var, period, changeexclude=None, excludetype=None):
    df = df.sort_values([byvar, period])
    df = df.dropna(subset=[var])
    prevbyvar = 'ZZZ'
    prevvarvalue = 0
    rowlist = []
    varvalues = df[[byvar, var]].values
    if (excludetype=="ratio" and changeexclude is not None):
        changeexclude = df[var].mean()*changeexclude
    for j in range(len(varvalues)):
        byvar = varvalues[j][0]
        varvalue = varvalues[j][1]
        if (prevbyvar!=byvar):
            if (prevbyvar!='ZZZ'):
                rowlist.append({'byvar':prevbyvar, 'avgvar':varsum/byvarcnt, 'sumvar':varsum, 'byvarcnt':byvarcnt})
            varsum = 0
            byvarcnt = 0
            prevbyvar = byvar
        if ((changeexclude is None) or (0 <= abs(varvalue-prevvarvalue) <= changeexclude) or (byvarcnt==0)):
            varsum += varvalue
            byvarcnt += 1
        prevvarvalue = varvalue
    rowlist.append({'byvar':prevbyvar, 'avgvar':varsum/byvarcnt, 'sumvar':varsum, 'byvarcnt':byvarcnt})
    return pd.DataFrame(rowlist)

将上述函数保存到 helperfunctions 子文件夹下的 combineagg.py 文件中。

2.3 导入模块并加载数据

我们导入 combineagg 模块,并加载相关的数据。以下是具体的代码:

sys.path.append(os.getcwd() + "/helperfunctions")
import combineagg as ca
coviddaily = pd.read_csv("data/coviddaily720.csv")
ltbrazil = pd.read_csv("data/ltbrazil.csv")
countries = pd.read_csv("data/ltcountries.csv")
locations = pd.read_csv("data/ltlocations.csv")
2.4 调用函数按组和时间段聚合值

我们将 coviddaily 数据框传递给 adjmeans 函数,按 location 分组并按 casedate 时间段聚合 new_cases 列的值。以下是调用代码:

ca.adjmeans(coviddaily, 'location', 'new_cases', 'casedate')

输出结果显示了每个国家的 new_cases 列的平均值、总和和计数。

2.5 再次调用函数并排除异常值

我们再次调用 adjmeans 函数,并指定排除 new_cases 列中相邻两天变化超过 150 的值。以下是调用代码:

ca.adjmeans(coviddaily, 'location', 'new_cases', 'casedate', 150)

输出结果显示,排除异常值后,一些国家的计数有所减少。

2.6 创建函数检查合并列的值

我们创建一个名为 checkmerge 的函数,该函数用于检查两个数据框中合并列的值。以下是函数的代码:

def checkmerge(dfleft, dfright, mergebyleft, mergebyright):
    dfleft['inleft'] = "Y"
    dfright['inright'] = "Y"
    dfboth = pd.merge(dfleft[[mergebyleft,'inleft']], dfright[[mergebyright,'inright']], left_on=[mergebyleft], right_on=[mergebyright], how="outer")
    dfboth.fillna('N', inplace=True)
    print(pd.crosstab(dfboth.inleft, dfboth.inright))
    print(dfboth.loc[(dfboth.inleft=='N') | (dfboth.inright=='N')].head(20))
2.7 调用函数检查合并列的值

我们将 countries locations 数据框传递给 checkmerge 函数,检查 countryid 列的值。以下是调用代码:

ca.checkmerge(countries.copy(), locations.copy(), "countryid", "countryid")

输出结果显示了两个数据框中 countryid 列的值的匹配情况。

2.8 创建函数合并文件夹中的所有 CSV 文件

我们创建一个名为 addfiles 的函数,该函数用于合并文件夹中的所有 CSV 文件。以下是函数的代码:

def addfiles(directory):
    dfout = pd.DataFrame()
    columnsmatched = True
    for filename in os.listdir(directory):
        if filename.endswith(".csv"): 
            fileloc = os.path.join(directory, filename)
            with open(fileloc) as f:
                dfnew = pd.read_csv(fileloc)
                print(filename + " has " + str(dfnew.shape[0]) + " rows.")
                dfout = pd.concat([dfout, dfnew])
                columndiff = dfout.columns.symmetric_difference(dfnew.columns)
                if (not columndiff.empty):
                    print("", "Different column names for:", filename, columndiff, "", sep="\n")
                    columnsmatched = False
    print("Columns Matched:", columnsmatched)
    return dfout
2.9 调用函数合并文件夹中的所有 CSV 文件

我们将 data/ltcountry 文件夹传递给 addfiles 函数,合并该文件夹中的所有 CSV 文件。以下是调用代码:

landtemps = ca.addfiles("data/ltcountry")
landtemps.country.value_counts()

输出结果显示了合并后的数据框中每个国家的行数。

通过以上步骤,我们可以看到如何使用自定义函数和类来实现数据清洗、检查异常值、聚合数据和合并数据等操作。这些函数和类可以提高代码的复用性和效率,使数据处理更加自动化和系统化。

自定义函数和类实现数据清洗自动化

3. 包含更新系列值逻辑的类

在处理特定数据集时,若数据会定期更新但结构相对稳定,且有大量列,使用类可以提高代码的可靠性和可读性。下面我们将创建一个 Respondent 类来处理 NLS 数据。

3.1 导入必要的库

我们需要导入一些必要的库,并将代码存储在 class_cleaning.py 文件中。以下是具体的代码:

import pandas as pd
import os
import sys
import pprint
3.2 创建 Respondent

创建 Respondent 类并将其保存到 helperfunctions 子文件夹下的 respondent.py 文件中。该类的 __init__ 方法会在实例化对象时自动运行,它接受一个字典作为参数,并将其赋值给实例变量 self.respdict ,同时增加一个计数器 respondentcnt 。以下是类的代码:

import math
import datetime as dt

class Respondent:
    respondentcnt = 0
    def __init__(self, respdict):
        self.respdict = respdict
        Respondent.respondentcnt += 1
3.3 添加计算子女数量的方法

Respondent 类添加一个 childnum 方法,用于计算受访者的子女总数。该方法通过将居住在家中的子女数量和不在家中的子女数量相加得到结果。以下是方法的代码:

def childnum(self):
    return self.respdict['childathome'] + self.respdict['childnotathome']
3.4 添加计算平均工作周数的方法

添加一个 avgweeksworked 方法,用于计算受访者在 20 年调查期间的平均工作周数。该方法使用字典推导式创建一个包含非缺失值的工作周数的字典,然后计算这些值的总和并除以字典的长度。以下是方法的代码:

def avgweeksworked(self):
    workdict = {k: v for k, v in self.respdict.items() if k.startswith('weeksworked') and not math.isnan(v)}
    nweeks = len(workdict)
    if (nweeks > 0):
        avgww = sum(workdict.values()) / nweeks
    else:
        avgww = 0
    return avgww
3.5 添加计算指定日期年龄的方法

添加一个 ageby 方法,用于计算受访者在指定日期的年龄。该方法将日期字符串转换为 datetime 对象,然后根据出生年份和月份计算年龄。以下是方法的代码:

def ageby(self, bydatestring):
    bydate = dt.datetime.strptime(bydatestring, '%Y%m%d')
    birthyear = self.respdict['birthyear']
    birthmonth = self.respdict['birthmonth']
    age = bydate.year - birthyear
    if (bydate.month < birthmonth or (bydate.month == birthmonth and bydate.day < 15)):
        age = age - 1
    return age
3.6 添加判断是否就读 4 年制大学的方法

添加一个 baenrollment 方法,用于判断受访者是否曾经就读于 4 年制大学。该方法使用字典推导式检查是否有任何大学入学值表明就读于 4 年制大学。以下是方法的代码:

def baenrollment(self):
    colenrdict = {k: v for k, v in self.respdict.items() if k.startswith('colenr') and v == "3. 4 - year college"}
    if (len(colenrdict) > 0):
        return "Y"
    else:
        return "N"
3.7 导入 Respondent

class_cleaning.py 文件中导入 Respondent 类。以下是导入代码:

sys.path.append(os.getcwd() + "/helperfunctions")
import respondent as rp
3.8 加载 NLS 数据并创建字典列表

加载 NLS 数据,并使用 to_dict 方法将其转换为字典列表。以下是具体的代码:

nls97 = pd.read_csv("data/nls97f.csv")
nls97list = nls97.to_dict('records')
nls97.shape
len(nls97list)
pprint.pprint(nls97list[0:1])

输出结果如下:

(8984, 89)
8984
[{'birthmonth': 5,
  'birthyear': 1980,
  'childathome': 4.0,
  'childnotathome': 0.0,
  'colenrfeb00': '1. Not enrolled',
  'colenrfeb01': '1. Not enrolled',
  ...
  'weeksworked16': 48.0,
  'weeksworked17': 48.0}]
3.9 循环创建 Respondent 实例

遍历字典列表,为每个字典创建一个 Respondent 实例,并使用实例方法获取所需的值,将这些值存储在一个新的字典中,然后将新字典添加到 analysislist 中。以下是具体的代码:

analysislist = []

for respdict in nls97list:
    resp = rp.Respondent(respdict)
    newdict = dict(originalid=respdict['originalid'],
                   childnum=resp.childnum(),
                   avgweeksworked=resp.avgweeksworked(),
                   age=resp.ageby('20201015'),
                   baenrollment=resp.baenrollment())
    analysislist.append(newdict)
3.10 将字典列表转换为 DataFrame

analysislist 传递给 pandas DataFrame 方法,创建一个新的数据框。首先,检查 analysislist 中的项目数量和创建的实例数量。以下是具体的代码:

len(analysislist)
resp.respondentcnt
pprint.pprint(analysislist[0:2])

输出结果如下:

8984
8984
[{'age': 40,
  'avgweeksworked': 49.05555555555556,
  'baenrollment': 'Y',
  'childnum': 4.0,
  'originalid': 8245},
 ...]

总结

通过以上内容,我们展示了如何使用自定义函数和类来实现数据清洗、检查异常值、聚合数据、合并数据以及更新系列值等操作。下面是一个简单的操作流程总结:

操作类型 操作步骤
检查变量分布和识别异常值 1. 导入必要的库并加载数据
2. 创建函数显示分布的重要属性
3. 调用函数检查变量分布
4. 创建函数列出数据框中的异常值
5. 调用函数列出异常值
6. 创建函数生成直方图和箱线图
7. 调用函数生成直方图和箱线图
数据聚合和合并 1. 导入必要的库
2. 创建函数按组和时间段聚合值
3. 导入模块并加载数据
4. 调用函数按组和时间段聚合值
5. 再次调用函数并排除异常值
6. 创建函数检查合并列的值
7. 调用函数检查合并列的值
8. 创建函数合并文件夹中的所有 CSV 文件
9. 调用函数合并文件夹中的所有 CSV 文件
包含更新系列值逻辑的类 1. 导入必要的库
2. 创建 Respondent
3. 添加计算子女数量的方法
4. 添加计算平均工作周数的方法
5. 添加计算指定日期年龄的方法
6. 添加判断是否就读 4 年制大学的方法
7. 导入 Respondent
8. 加载 NLS 数据并创建字典列表
9. 循环创建 Respondent 实例
10. 将字典列表转换为 DataFrame

以下是一个 mermaid 流程图,展示了整个数据处理的主要流程:

graph LR
    A[数据加载] --> B[检查变量分布和异常值]
    B --> C[数据聚合和合并]
    C --> D[使用类更新系列值]

这些自定义函数和类可以提高代码的复用性和效率,使数据处理更加自动化和系统化。在实际应用中,我们可以根据具体需求对这些函数和类进行扩展和修改。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值