pandas打卡第六章缺失数据的处理

本文深入探讨Pandas 1.0中缺失数据的处理,包括了解缺失信息、三种缺失符号(np.nan, None, NaT)及其特性、Nullable类型与NA符号的引入,以及填充、剔除、插值等方法。还介绍了如何通过convert_dtypes方法转换数据类型,以及在缺失数据的运算与分组中的处理策略。" 131657543,19419959,模拟退火算法解决流水车间调度问题,"['算法', '机器学习', '优化', '模拟退火']

第6章 缺失数据的处理

在接下来的两章中,会接触到数据预处理中比较麻烦的类型,即缺失数据和文本数据(尤其是混杂型文本)
Pandas在步入1.0后,对数据类型也做出了新的尝试,尤其是Nullable类型和String类型,了解这些可能在未来成为主流的新特性是必要的
import pandas as pd
import numpy as np
df = pd.read_csv('./table_missing.csv')
df.head()#观察数据
SchoolClassIDGenderAddressHeightWeightMathPhysics
0S_1C_1NaNMstreet_1173NaN34.0A+
1S_1C_1NaNFstreet_2192NaN32.5B+
2S_1C_11103.0Mstreet_2186NaN87.2B+
3S_1NaNNaNFstreet_216781.080.4NaN
4S_1C_11105.0NaNstreet_415964.084.8A-

一、缺失观测及其类型

1. 了解缺失信息

(a)isna和notna方法
对Series使用会返回布尔列表
df['Physics'].isna().head()
0    False
1    False
2    False
3     True
4    False
Name: Physics, dtype: bool
df['Physics'].notna().head()
0     True
1     True
2     True
3    False
4     True
Name: Physics, dtype: bool
对DataFrame使用会返回布尔表
df.isna().head()
SchoolClassIDGenderAddressHeightWeightMathPhysics
0FalseFalseTrueFalseFalseFalseTrueFalseFalse
1FalseFalseTrueFalseFalseFalseTrueFalseFalse
2FalseFalseFalseFalseFalseFalseTrueFalseFalse
3FalseTrueTrueFalseFalseFalseFalseFalseTrue
4FalseFalseFalseTrueFalseFalseFalseFalseFalse
但对于DataFrame我们更关心到底每列有多少缺失值
df.isna().sum()
School      0
Class       4
ID          6
Gender      7
Address     0
Height      0
Weight     13
Math        5
Physics     4
dtype: int64
此外,可以通过第1章中介绍的info函数查看缺失信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 35 entries, 0 to 34
Data columns (total 9 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   School   35 non-null     object 
 1   Class    31 non-null     object 
 2   ID       29 non-null     float64
 3   Gender   28 non-null     object 
 4   Address  35 non-null     object 
 5   Height   35 non-null     int64  
 6   Weight   22 non-null     float64
 7   Math     30 non-null     float64
 8   Physics  31 non-null     object 
dtypes: float64(3), int64(1), object(5)
memory usage: 2.6+ KB
(b)查看缺失值的所以在行
以最后一列为例,挑出该列缺失值的行
df[df['Physics'].isna()]
SchoolClassIDGenderAddressHeightWeightMathPhysics
3S_1NaNNaNFstreet_216781.080.4NaN
8S_1C_21204.0Fstreet_516263.033.8NaN
13S_1C_31304.0NaNstreet_219570.085.2NaN
22S_2C_22203.0Mstreet_415591.073.8NaN
(c)挑选出所有非缺失值列
使用all就是全部非缺失值,如果是any就是至少有一个不是缺失值
df[df.notna().all(1)]
SchoolClassIDGenderAddressHeightWeightMathPhysics
5S_1C_21201.0Mstreet_515968.097.0A-
6S_1C_21202.0Fstreet_417694.063.5B-
12S_1C_31303.0Mstreet_718882.049.7B
17S_2C_12103.0Mstreet_415761.052.5B-
21S_2C_22202.0Fstreet_719477.068.5B+
25S_2C_32301.0Fstreet_415778.072.3B+
27S_2C_32303.0Fstreet_719099.065.9C
28S_2C_32304.0Fstreet_616481.095.5A-
29S_2C_32305.0Mstreet_418773.048.9B

2. 三种缺失符号

(a)np.nan
np.nan是一个麻烦的东西,首先它不等与任何东西,甚至不等于自己
np.nan == np.nan
False
np.nan == 0
False
np.nan == None
False
在用equals函数比较时,自动略过两侧全是np.nan的单元格,因此结果不会影响
df.equals(df)
True
其次,它在numpy中的类型为浮点,由此导致数据集读入时,即使原来是整数的列,只要有缺失值就会变为浮点型
type(np.nan)
float
pd.Series([1,2,3]).dtype
dtype('int64')
pd.Series([1,np.nan,3]).dtype
dtype('float64')
此外,对于布尔类型的列表,如果是np.nan填充,那么它的值会自动变为True而不是False
pd.Series([1,np.nan,3],dtype='bool')
0    True
1    True
2    True
dtype: bool
但当修改一个布尔列表时,会改变列表类型,而不是赋值为True
s = pd.Series([True,False],dtype='bool')
s[1]=np.nan
s
0    1.0
1    NaN
dtype: float64
在所有的表格读取后,无论列是存放什么类型的数据,默认的缺失值全为np.nan类型
因此整型列转为浮点;而字符由于无法转化为浮点,因此只能归并为object类型(‘O’),原来是浮点型的则类型不变
df['ID'].dtype
dtype('float64')
df['Math'].dtype
dtype('float64')
df['Class'].dtype
dtype('O')
(b)None
None比前者稍微好些,至少它会等于自身
None == None
True
它的布尔值为False
pd.Series([None],dtype='bool')
0    False
dtype: bool
修改布尔列表不会改变数据类型
s = pd.Series([True,False],dtype='bool')
s[0]=None
s
0    False
1    False
dtype: bool
s = pd.Series([1,0],dtype='bool')
s[0]=None
s
0    False
1    False
dtype: bool
在传入数值类型后,会自动变为np.nan
type(pd.Series([1,None])[1])
numpy.float64
只有当传入object类型是保持不动,几乎可以认为,除非人工命名None,它基本不会自动出现在Pandas中
type(pd.Series([1,None],dtype='O')[1])
NoneType
在使用equals函数时不会被略过,因此下面的情况下返回False
pd.Series([None]).equals(pd.Series([np.nan]))
False
(c)NaT
NaT是针对时间序列的缺失值,是Pandas的内置类型,可以完全看做时序版本的np.nan,与自己不等,且使用equals是也会被跳过
s_time = pd.Series([pd.Timestamp('20120101')]*5)
s_time
0   2012-01-01
1   2012-01-01
2   2012-01-01
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
s_time[2] = None
s_time
0   2012-01-01
1   2012-01-01
2          NaT
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
s_time[2] = np.nan
s_time
0   2012-01-01
1   2012-01-01
2          NaT
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
s_time[2] = pd.NaT
s_time
0   2012-01-01
1   2012-01-01
2          NaT
3   2012-01-01
4   2012-01-01
dtype: datetime64[ns]
type(s_time[2])
pandas._libs.tslibs.nattype.NaTType
s_time[2] == s_time[2]
False
s_time.equals(s_time)
True
s = pd.Series([True,False],dtype='bool')
s[1]=pd.NaT
s
0    True
1    True
dtype: bool

3. Nullable类型与NA符号

这是Pandas在1.0新版本中引入的重大改变,其目的就是为了(在若干版本后)解决之前出现的混乱局面,统一缺失值处理方法
“The goal of pd.NA is provide a “missing” indicator that can be used consistently across data types (instead of np.nan, None or pd.NaT depending on the data type).”——User Guide for Pandas v-1.0
官方鼓励用户使用新的数据类型和缺失类型pd.NA
(a)Nullable整形
对于该种类型而言,它与原来标记int上的符号区别在于首字母大写:‘Int’
s_original = pd.Series([1, 2], dtype="int64")
s_original
0    1
1    2
dtype: int64
s_new = pd.Series([1, 2], dtype="Int64")
s_new
0    1
1    2
dtype: Int64
它的好处就在于,其中前面提到的三种缺失值都会被替换为统一的NA符号,且不改变数据类型
s_original[1] = np.nan
s_original
0    1.0
1    NaN
dtype: float64
s_new[1] = np.nan
s_new
0       1
1    <NA>
dtype: Int64
s_new[1] = None
s_new
0       1
1    <NA>
dtype: Int64
s_new[1] = pd.NaT
s_new
0       1
1    <NA>
dtype: Int64
(b)Nullable布尔
对于该种类型而言,作用与上面的类似,记号为boolean
s_original = pd.Series([1, 0], dtype="bool")
s_original
0     True
1    False
dtype: bool
s_new = pd.Series([0, 1], dtype="boolean")
s_new
0    False
1     True
dtype: boolean
s_original[0] = np.nan
s_original
0    NaN
1    0.0
dtype: float64
s_original = pd.Series([1, 0], dtype="bool") #此处重新加一句是因为前面赋值改变了bool类型
s_original[0] = None
s_original
0    False
1    False
dtype: bool
s_new[0] = np.nan
s_new
0    <NA>
1    True
dtype: boolean
s_new[0] = None
s_new
0    <NA>
1    True
dtype: boolean
s_new[0] = pd.NaT
s_new
0    <NA>
1    True
dtype: boolean
需要注意的是,含有pd.NA的布尔列表在1.0.2之前的版本作为索引时会报错,这是一个之前的bug,现已经修复
s = pd.Series(['dog','cat'])
s[s_new]
1    cat
dtype: object
(c)string类型
该类型是1.0的一大创新,目的之一就是为了区分开原本含糊不清的object类型,这里将简要地提及string,因为它是第7章的主题内容
它本质上也属于Nullable类型,因为并不会因为含有缺失而改变类型
s = pd.Series(['dog','cat'],dtype='string')
s
0    dog
1    cat
dtype: string
s[0] = np.nan
s
0    <NA>
1     cat
dtype: string
s[0] = None
s
0    <NA>
1     cat
dtype: string
s[0] = pd.NaT
s
0    <NA>
1     cat
dtype: string
此外,和object类型的一点重要区别就在于,在调用字符方法后,string类型返回的是Nullable类型,object则会根据缺失类型和数据类型而改变
s = pd.Series(["a", None, "b"], dtype="string")
s.str.count('a')
0       1
1    <NA>
2       0
dtype: Int64
s2 = pd.Series(["a", None, "b"], dtype="object")
s2.str.count("a")
0    1.0
1    NaN
2    0.0
dtype: float64
s.str.isdigit()
0    False
1     <NA>
2    False
dtype: boolean
s2.str.isdigit()
0    False
1     None
2    False
dtype: object

4. NA的特性

(a)逻辑运算
只需看该逻辑运算的结果是否依赖pd.NA的取值,如果依赖,则结果还是NA,如果不依赖,则直接计算结果
True | pd.NA
True
pd.NA | True
True
False | pd.NA
<NA>
False & pd.NA
False
True & pd.NA
<NA>
取值不明直接报错
#bool(pd.NA)
(b)算术运算和比较运算
这里只需记住除了下面两类情况,其他结果都是NA即可
pd.NA ** 0
1
1 ** pd.NA
1
其他情况:
pd.NA + 1
<NA>
"a" * pd.NA
<NA>
pd.NA == pd.NA
<NA>
pd.NA < 2.5
<NA>
np.log(pd.NA)
<NA>
np.add(pd.NA, 1)
<NA>

5. convert_dtypes方法

这个函数的功能往往就是在读取数据时,就把数据列转为Nullable类型,是1.0的新函数
pd.read_csv('data/table_missing.csv').dtypes
School      object
Class       object
ID         float64
Gender      object
Address     object
Height       int64
Weight     float64
Math       float64
Physics     object
dtype: object
pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes
School      string
Class       string
ID           Int64
Gender      string
Address     string
Height       Int64
Weight       Int64
Math       float64
Physics     string
dtype: object

二、缺失数据的运算与分组

1. 加号与乘号规则

使用加法时,缺失值为0
s = pd.Series([2,3,np.nan,4])
s.sum()
9.0
使用乘法时,缺失值为1
s.prod()
24.0
使用累计函数时,缺失值自动略过
s.cumsum()
0    2.0
1    5.0
2    NaN
3    9.0
dtype: float64
s.cumprod()
0     2.0
1     6.0
2     NaN
3    24.0
dtype: float64
s.pct_change()
0         NaN
1    0.500000
2    0.000000
3    0.333333
dtype: float64

2. groupby方法中的缺失值

自动忽略为缺失值的组
df_g = pd.DataFrame({'one':['A','B','C','D',np.nan],'two':np.random.randn(5)})
df_g
onetwo
0A-1.126645
1B0.924595
2C-2.076309
3D-0.312150
4NaN0.961543
df_g.groupby('one').groups
{'A': Int64Index([0], dtype='int64'),
 'B': Int64Index([1], dtype='int64'),
 'C': Int64Index([2], dtype='int64'),
 'D': Int64Index([3], dtype='int64')}

三、填充与剔除

1. fillna方法

(a)值填充与前后向填充(分别与ffill方法和bfill方法等价)
df['Physics'].fillna('missing').head()
0         A+
1         B+
2         B+
3    missing
4         A-
Name: Physics, dtype: object
df['Physics'].fillna(method='ffill').head()
0    A+
1    B+
2    B+
3    B+
4    A-
Name: Physics, dtype: object
df['Physics'].fillna(method='backfill').head()
0    A+
1    B+
2    B+
3    A-
4    A-
Name: Physics, dtype: object
(b)填充中的对齐特性
df_f = pd.DataFrame({'A':[1,3,np.nan],'B':[2,4,np.nan],'C':[3,5,np.nan]})
df_f.fillna(df_f.mean())
ABC
01.02.03.0
13.04.05.0
22.03.04.0
返回的结果中没有C,根据对齐特点不会被填充
df_f.fillna(df_f.mean()[['A','B']])
ABC
01.02.03.0
13.04.05.0
22.03.0NaN

2. dropna方法

(a)axis参数
df_d = pd.DataFrame({'A':[np.nan,np.nan,np.nan],'B':[np.nan,3,2],'C':[3,2,1]})
df_d
ABC
0NaNNaN3
1NaN3.02
2NaN2.01
df_d.dropna(axis=0)
ABC
df_d.dropna(axis=1)
C
03
12
21
(b)how参数(可以选all或者any,表示全为缺失去除和存在缺失去除)
df_d.dropna(axis=1,how='all')
BC
0NaN3
13.02
22.01
(c)subset参数(即在某一组列范围中搜索缺失值)
df_d.dropna(axis=0,subset=['B','C'])
ABC
1NaN3.02
2NaN2.01

四、插值(interpolation)

1. 线性插值

(a)索引无关的线性插值
默认状态下,interpolate会对缺失的值进行线性插值
s = pd.Series([1,10,15,-5,-2,np.nan,np.nan,28])
s
0     1.0
1    10.0
2    15.0
3    -5.0
4    -2.0
5     NaN
6     NaN
7    28.0
dtype: float64
s.interpolate()
0     1.0
1    10.0
2    15.0
3    -5.0
4    -2.0
5     8.0
6    18.0
7    28.0
dtype: float64
s.interpolate().plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7df20af50>

在这里插入图片描述

此时的插值与索引无关
s.index = np.sort(np.random.randint(50,300,8))
s.interpolate()
#值不变
69      1.0
71     10.0
84     15.0
117    -5.0
119    -2.0
171     8.0
219    18.0
236    28.0
dtype: float64
s.interpolate().plot()
#后面三个点不是线性的(如果几乎为线性函数,请重新运行上面的一个代码块,这是随机性导致的)
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dfc69890>

在这里插入图片描述

(b)与索引有关的插值
method中的index和time选项可以使插值线性地依赖索引,即插值为索引的线性函数
s.interpolate(method='index').plot()
#可以看到与上面的区别
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dca0c4d0>

在这里插入图片描述

如果索引是时间,那么可以按照时间长短插值,对于时间序列将在第9章详细介绍
s_t = pd.Series([0,np.nan,10]
        ,index=[pd.Timestamp('2012-05-01'),pd.Timestamp('2012-05-07'),pd.Timestamp('2012-06-03')])
s_t
2012-05-01     0.0
2012-05-07     NaN
2012-06-03    10.0
dtype: float64
s_t.interpolate().plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc964850>

在这里插入图片描述

s_t.interpolate(method='time').plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc8eda10>

在这里插入图片描述

2. 高级插值方法

此处的高级指的是与线性插值相比较,例如样条插值、多项式插值、阿基玛插值等(需要安装Scipy),方法详情请看这里
关于这部分仅给出一个官方的例子,因为插值方法是数值分析的内容,而不是Pandas中的基本知识:
ser = pd.Series(np.arange(1, 10.1, .25) ** 2 + np.random.randn(37))
missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
ser[missing] = np.nan
methods = ['linear', 'quadratic', 'cubic']
df = pd.DataFrame({m: ser.interpolate(method=m) for m in methods})
df.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x7fe7dc86f810>

在这里插入图片描述

3. interpolate中的限制参数

(a)limit表示最多插入多少个
s = pd.Series([1,np.nan,np.nan,np.nan,5])
s.interpolate(limit=2)
0    1.0
1    2.0
2    3.0
3    NaN
4    5.0
dtype: float64
(b)limit_direction表示插值方向,可选forward,backward,both,默认前向
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_direction='backward')
0    1.0
1    1.0
2    1.0
3    2.0
4    3.0
5    4.0
6    5.0
7    NaN
8    NaN
dtype: float64
(c)limit_area表示插值区域,可选inside,outside,默认None
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_area='inside')
0    NaN
1    NaN
2    1.0
3    2.0
4    3.0
5    4.0
6    5.0
7    NaN
8    NaN
dtype: float64
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_area='outside')
0    NaN
1    NaN
2    1.0
3    NaN
4    NaN
5    NaN
6    5.0
7    5.0
8    5.0
dtype: float64

五、问题与练习

1. 问题

【问题一】 如何删除缺失值占比超过25%的列?
【问题二】 什么是Nullable类型?请谈谈为什么要引入这个设计?
【问题三】 对于一份有缺失值的数据,可以采取哪些策略或方法深化对它的了解?

问题一:data=data.drop(data.columns[data.isna().sum()/data.shape[0]>0.25],axis=1)
问题二:Nullable类型目的是为了解决之前出现的混乱局面,统一缺失值处理方法。
(1)Nullable整形与原来标记int上的符号区别在于首字母大写:‘Int’,它的好处就在于,其中前面提到的三种缺失值都会被替换为统一的NA符号,且不改变数据类型.
(2)Nullable布尔作用与上面的类似,记号为boolean.
(3)string类型目的之一就是为了区分开原本含糊不清的object类型,它本质上也属于Nullable类型,因为并不会因为含有缺失而改变类型,此外,和object类型的一点重要区别在于,string类型返回的是Nullable类型,object则会根据缺失类型和数据类型而改变.
问题三:data.info(),data.describe(),data.isna().sum(),data[data.notna().all(1)],data[data.notna().any(1)]

2. 练习

【练习一】现有一份虚拟数据集,列类型分别为string/浮点/整型,请解决如下问题:
(a)请以列类型读入数据,并选出C为缺失值的行。
(b)现需要将A中的部分单元转为缺失值,单元格中的最小转换概率为25%,且概率大小与所在行B列单元的值成正比。

a问题答案如下:

#a:
data=pd.read_csv('./missing_data_one.csv').head()
data[data['C'].isna()]
ABC
1not_NaN0.7NaN

b问题答案如下:

#b:
min_b=data['B'].min()
for i in range(data.shape[0]):
    if np.random.rand()<=(data.loc[i]['B']*0.25/min_b):
        data.loc[i,'A']=np.nan
data
ABC
0not_NaN0.9224.0
1not_NaN0.700NaN
2NaN0.5038.0
3not_NaN0.9384.0
4NaN0.95210.0
【练习二】 现有一份缺失的数据集,记录了36个人来自的地区、身高、体重、年龄和工资,请解决如下问题:
(a)统计各列缺失的比例并选出在后三列中至少有两个非缺失值的行。
(b)请结合身高列和地区列中的数据,对体重进行合理插值。
data=pd.read_csv('missing_data_two.csv')

a问题答案如下:

#a问题解答:
print(data.isna().sum()/data.shape[0])
data[data.iloc[:,-3:].isna().sum(axis=1)>=2]
编号    0.000000
地区    0.000000
身高    0.000000
体重    0.222222
年龄    0.250000
工资    0.222222
dtype: float64
编号地区身高体重年龄工资
23C169.0962.18NaNNaN
1112A202.5692.30NaNNaN
1213C177.37NaN79.0NaN
1415C199.1189.20NaNNaN
2627B158.28NaN51.0NaN
3233C181.01NaNNaN13021.0
3334A196.6787.00NaNNaN

b问题要分不同地区对体重按身高进行线性插值

#b问题解答:
#A地区体重按身高插值
data_A=data.loc[data['地区']=='A']
data_A.loc[0,:],data_A.loc[3,:]=data_A.loc[3,:],data_A.loc[0,:]
data_A.index=data_A['身高']
%matplotlib inline
data_A
H:\aanaconda3\lib\site-packages\pandas\core\indexing.py:966: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self.obj[item] = s
编号地区身高体重年龄工资
身高
166.614A166.6159.9577.05434.0
157.501A157.50NaN47.015905.0
187.136A187.1378.4255.013959.0
183.808A183.8075.4248.019779.0
202.5612A202.5692.30NaNNaN
165.6816A165.68NaN46.013683.0
172.8319A172.8365.5523.013768.0
154.6322A154.6349.1735.014559.0
165.5524A165.55NaN66.019890.0
164.4326A164.4357.9934.019899.0
183.7330A183.7375.3658.08270.0
196.6734A196.6787.00NaNNaN
data_A['体重'].interpolate(method='index').plot()
data_A['体重'].interpolate(method='index',inplace=True)
H:\aanaconda3\lib\site-packages\pandas\core\generic.py:7023: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._update_inplace(new_data)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0, flags=flags)

在这里插入图片描述

data_A
编号地区身高体重年龄工资
身高
166.614A166.6159.95000077.05434.0
157.501A157.5051.75300047.015905.0
187.136A187.1378.42000055.013959.0
183.808A183.8075.42000048.019779.0
202.5612A202.5692.300000NaNNaN
165.6816A165.6859.11385346.013683.0
172.8319A172.8365.55000023.013768.0
154.6322A154.6349.17000035.014559.0
165.5524A165.5558.99697266.019890.0
164.4326A164.4357.99000034.019899.0
183.7330A183.7375.36000058.08270.0
196.6734A196.6787.000000NaNNaN
#B地区体重按身高插值
data_B=data.loc[data['地区']=='B']
data_B.index=data_B['身高']
data_B
编号地区身高体重年龄工资
身高
202.002B202.0091.8025.0NaN
185.195B185.19NaN62.04242.0
179.679B179.6771.7065.08608.0
163.4111B163.4157.07NaN6495.0
175.9914B175.9968.39NaN13130.0
166.4817B166.4859.8331.017673.0
156.9920B156.9951.2962.03054.0
157.8723B157.8752.0867.07398.0
158.2827B158.28NaN51.0NaN
162.1229B162.1255.91NaN13362.0
167.2832B167.2860.5564.018317.0
170.1235B170.1263.1177.07398.0
data_B['体重'].interpolate(method='index').plot()
data_B['体重'].interpolate(method='index',inplace=True)
H:\aanaconda3\lib\site-packages\pandas\core\generic.py:7023: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._update_inplace(new_data)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0, flags=flags)

在这里插入图片描述

data_B
编号地区身高体重年龄工资
身高
202.002B202.0091.80000025.0NaN
185.195B185.1976.66874262.04242.0
179.679B179.6771.70000065.08608.0
163.4111B163.4157.070000NaN6495.0
175.9914B175.9968.390000NaN13130.0
166.4817B166.4859.83000031.017673.0
156.9920B156.9951.29000062.03054.0
157.8723B157.8752.08000067.07398.0
158.2827B158.2852.44948251.0NaN
162.1229B162.1255.910000NaN13362.0
167.2832B167.2860.55000064.018317.0
170.1235B170.1263.11000077.07398.0
#C地区体重按身高插值
data_C=data.loc[data['地区']=='C']
data_C.index=data_C['身高']
data_C
编号地区身高体重年龄工资
身高
169.093C169.0962.18NaNNaN
163.817C163.8157.4343.06533.0
186.0810C186.0877.4765.012433.0
177.3713C177.37NaN79.0NaN
199.1115C199.1189.20NaNNaN
191.6218C191.6282.46NaN12447.0
200.2221C200.2290.2041.0NaN
181.7825C181.7873.6063.011383.0
172.3928C172.3965.1543.010362.0
181.1931C181.19NaN41.012616.0
181.0133C181.01NaNNaN13021.0
180.4736C180.4772.4278.09554.0
data_C['体重'].interpolate(method='index').plot()
data_C['体重'].interpolate(method='index',inplace=True)
H:\aanaconda3\lib\site-packages\pandas\core\generic.py:7023: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._update_inplace(new_data)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0.0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 36523 missing from current font.
  font.set_text(s, 0, flags=flags)
H:\aanaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 39640 missing from current font.
  font.set_text(s, 0, flags=flags)

在这里插入图片描述

data_C
编号地区身高体重年龄工资
身高
169.093C169.0962.180000NaNNaN
163.817C163.8157.43000043.06533.0
186.0810C186.0877.47000065.012433.0
177.3713C177.3769.63076779.0NaN
199.1115C199.1189.200000NaNNaN
191.6218C191.6282.460000NaN12447.0
200.2221C200.2290.20000041.0NaN
181.7825C181.7873.60000063.011383.0
172.3928C172.3965.15000043.010362.0
181.1931C181.1973.06855041.012616.0
181.0133C181.0172.906412NaN13021.0
180.4736C180.4772.42000078.09554.0

总结:

1.对待缺失值可先从整体,而后具体至行,列去观察研究

2.对于缺失值较多的数据可直接删除

3.对于可容忍缺失数量的特征可进行线性插值,中位数,众数填充等方法

4.注意不同缺失值的变量类型有所不同,其特性也各不相同,处理时尽量转化成Nullable类型


<think>嗯,用户问的是如何将打卡机数据表转换成工时考勤表的程序。首先,我需要理解用户的需求。打卡机数据通常记录员工的上下班时间,但原始数据可能杂乱,包含重复记录、异常时间等,用户需要处理这些数据,生成规范的考勤表,用来计算工时、迟到、早退等情况。 接下来,我应该考虑整个流程的步骤。数据导入是第一步,可能需要处理Excel、CSV或者数据库中的数据。然后数据清洗,处理重复、缺失或异常的时间记录。比如,同一天多次打卡,可能需要确定有效的上下班时间。 然后,工时计算是关键。需要根据打卡时间计算每天的出勤时长,还要考虑迟到和早退的情况。这里可能需要设置工作时间段,比如9:00-18:00,午休时间扣除。另外,还要处理加班的情况,比如18:00之后的打卡算作加班。 接下来是生成考勤表,这一步需要将处理后的数据整理成规范的表格,可能包括日期、上班时间、下班时间、工时、迟到分钟数、早退分钟数、加班时长等。最后导出结果,供HR使用。 然后需要考虑技术实现。数据导入可能需要用Python的pandas库,或者Excel VBA。数据清洗部分需要处理时间格式,去重,筛选有效记录。工时计算需要时间差计算,条件判断迟到早退。生成考勤表可能需要用pandas的DataFrame或者Excel操作。 可能遇到的难点包括跨天打卡,比如夜班员工,处理这种情况需要特别判断。还有调休、请假的情况,可能需要人工标记或者集成其他系统的数据。此外,不同公司的考勤规则可能不同,程序需要有一定的灵活性,比如可配置的工作时间段、迟到阈值等。 安全性方面,考勤数据涉及员工隐私,程序需要确保数据安全,防止泄露。异常处理机制也很重要,比如无法识别的打卡记录需要记录日志,供人工检查。 最后,用户可能需要一个自动化脚本或者可视化工具,方便非技术人员使用。可能需要考虑界面设计,或者定期自动运行生成报表。 总结下来,整个程序需要模块化处理,分步骤解决数据导入、清洗、计算和导出,同时考虑各种异常情况和不同的考勤规则,确保准确性和灵活性。</think>我将分步骤为您解析如何设计打卡机数据转工时考勤表的程序: 一、原始数据处理阶段 1. 数据导入模块 - 支持常见格式:Excel、CSV、数据库直连 - 示例数据结构: $$员工编号 | 打卡时间 | 设备编号$$ $$1001 | 2023-08-01 08:58:12 | D03$$ 2. 数据清洗流程 (1) 去重处理:合并同一分钟内多次打卡 (2) 异常值筛选:过滤非工作时间打卡(如凌晨记录) (3) 时间格式标准化:统一为`YYYY-MM-DD HH:mm:ss` 二、核心计算逻辑 1. 有效打卡识别 - 每日最少2条记录(上班+下班) - 跨天处理(适用于夜班): $$有效日期 = 打卡时间 < 04:00 ? 前一天 : 当天$$ 2. 工时计算模型 - 基准工作时间段设定: ```python work_start = time(9,0) # 上午9点 work_end = time(18,0) # 下午6点 lunch_break = 1.5 # 午休1.5小时 ``` - 实际工时公式: $$实际工时 = \frac{\text{下班时间 - 上班时间}}{3600} - lunch\_break$$ 3. 异常考勤判定 - 迟到判定:`上班打卡 > work_start + 允许误差(如10分钟)` - 早退判定:`下班打卡 < work_end - 允许误差` 三、输出考勤表结构 ``` | 姓名 | 日期 | 上班时间 | 下班时间 | 工时 | 迟到(min) | 早退(min) | 加班(h) | |--------|------------|-----------|-----------|------|-----------|-----------|---------| | 张三 | 2023-08-01 | 08:58 | 18:05 | 8.0 | 0 | 0 | 0.08 | | 李四 | 2023-08-01 | 09:15 | 17:45 | 7.5 | 15 | 15 | 0 | ``` 四、技术实现方案 1. 推荐工具组合 - Python + pandas:适合处理百万级数据 - SQL + 存储过程:适用于已有数据库系统 - Excel Power Query:适合小型企业快速实现 2. 关键代码片段(Python示例): ```python def calculate_work_hours(start, end): # 跨日处理 if end < start: end += timedelta(days=1) duration = (end - start).seconds / 3600 return max(0, duration - 1.5) # 扣除午休 ``` 五、特殊场景处理 1. 补卡机制:人工标记异常数据 2. 弹性工时:设置个性化时段参数 3. 假期标记:集成法定节假日日历 4. 审批关联:对接OA系统请假数据 六、优化方向建议 1. 可视化看板:生成月度考勤汇总图表 2. 自动预警:当日异常打卡实时通知 3. 移动端支持:结合GPS定位验证 4. 生物识别:集成人脸/指纹二次验证 该程序需根据企业具体考勤规则进行参数配置,建议初期保留人工复核环节,待系统稳定后逐步实现全自动化处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值