数据的拼接与缺失值的处理

数据缺失值处理

导包

import numpy as np
import pandas as pd
import os
import missingno as msno

1.演示缺失值查看和比较

# 细节: 在Pandas中, 缺失值来源于numpy包的NAN, nan, NaN, 他们都表示空.
# from numpy import NAN, nan, NaN

# 1. 空值比较.
print(np.NAN == True)   # False
print(np.NAN == False)  # False
print(np.NAN == '') # False
print(np.NAN == 0)  # False
# 2. 空和空比较, 也都是False.
print(np.NAN == np.nan) # False
print(np.NAN == np.NaN) # False
print(np.nan == np.NaN) # False
# 3. 判断是否为空.  Pandas库的 isnull(), isna(),  notnull(), notna()
print(pd.isnull(np.NAN))    # True
print(pd.isnull(''))        # False

print(pd.notnull(np.NAN))    # False
print(pd.notnull(''))        # True

2.加载数据时,操作缺失值

  • keep_default_na = true/false 是否加载缺失值
  • na_values = [] 手动设置缺失值
# 1. 读取数据, 获取df对象.
pd.read_csv('data/survey_visited.csv')      # 默认: 会加载缺失值.

# keep_default_na: 设置加载时是否加载缺失值.  True(默认): 加载.  False: 不加载.
pd.read_csv('data/survey_visited.csv', keep_default_na=False)

# na_values: 设置加载时, 哪些值 设置为缺失值.
pd.read_csv('data/survey_visited.csv', na_values=['734', '751', 'MSK-4', '1939-01-07'])

3.演示:删除,填充缺失值-泰塔尼克号-数据集

3.1加载数据,并查看df对象的基本统计信息

# 1. 加载数据, 获取df对象.
train = pd.read_csv('data/titanic_train.csv')
train
# 2. 查看数据的常用 统计值.
train.shape         # (891, 12), 行列数
train.info()        # 基本信息
train.describe()    # 统计信息.
# 3. 报表的形式, 查看缺失值. 
msno.bar(train)     # 柱状图
msno.heatmap(train) # 查看缺失值之间的关联性. 

3.2删除缺失值

  • dropna(axis=‘rows’) 按行删除缺失值
  • dropna(axis=‘columns’) 按列删除缺失值
    • 指定行和列时格式 :subset= [列名1,列名2] ,how = ‘all | any’
# 1. 查看 df 对象
train.isnull().sum()        # 查看各列的空值情况.
train.shape                 # (891, 12)
# 2. 删除缺失值.
# 按 行 删除缺失值.
train.dropna()
train.dropna(axis='rows')   # 效果同上, 默认按照: 行 删除空值.
train.dropna(axis=0)        # 效果同上, 默认按照: 行 删除空值.

# 按 列 删除缺失值
train.dropna(axis='columns')    # 按列删除缺失值.
train.dropna(axis=1)            # 效果同上.
# subset参数: 参考的列, 即: 该列值为空, 才会删除行, 或者 列.
# how参数: 删除方式, any: 只要有空值, 就删除行或者列. all: 全部为空, 才删除行或者列.
train.dropna(subset=['Age', 'Embarked'], how='all')     
train.dropna(subset=['Age', 'Embarked'], how='any')     

# 查看删除后的数据
train.dropna(subset=['Age', 'Embarked'], how='any').isnull().sum()

3.3填充缺失值

3.3.1非时间序列数据,即:固定值
  • 固定值填充 : fillna()
  • 空值上一个值填充: ffill()
  • 空值下一个值填充: bfill()
  • 线性插入法, 即: 结合上下值, 计算出结果, 并填充: interpolate(limit_direction = ‘both | forward | backward’)
  • both :结合上下值
  • forword : 结合上一个值
  • backword: 结合下一个值
#1.查看数据
train.isnull().sun()
#2.填充缺失值,用固定值填充
train.fillna(0)   #用0填充
#3.查看填充后的数据集
train.fillna(0).isnull().sum()
3.3.2时间序列填充
# 时间序列填充 = 结合数据列的上下文的值, 计算出要填充的值. 
# 1. 加载数据, 获取df对象. 
city_day = pd.read_csv('data/city_day.csv', parse_dates=['Date'], index_col='Date')
city_day
# 2. 从中提取出一些数据, 二甲苯列的数据. 
city_day.Xylene[50:64]
# 3. 采用 时间序列填充, 参考: 空值的上一个值.
# city_day.Xylene[50:64].fillna(method='ffill')       # 已过时.
city_day.Xylene[50:64].ffill()                        # 推荐写法
# 4. 采用 时间序列填充, 参考: 空值的下一个值.
# city_day.Xylene[50:64].fillna(method='bfill')       # 已过时.
city_day.Xylene[50:64].bfill()                     # 推荐写法
city_day.Xylene[50:64].bfill().plot()              # 图形化展示
# 5. 采用 线性插入法, 即: 结合上下值, 计算出结果, 并填充.
# both: 结合上下值.  forward: 结合上1个值, backward: 结合下一个值.
city_day.interpolate(limit_direction='forward').Xylene[50:64]

数据拼接

import numpy as np
import pandas as pd
import os
import sqlite3

1.演示 concat()函数

1.1加载数据

df1 = pd.read_csv('data/concat_1.csv')
df2 = pd.read_csv('data/concat_2.csv')
df3  = pd.read_csv('data/concat_3.csv')
df1
df2
df3

1.2 df对象和df对象的拼接

  • concat()行列拼接
# 记忆: concat()函数既能实现行拼接(默认), 也能实现列拼接.   行拼接参考: 列名,   列拼接参考: 索引列(行索引)
# 1. 演示行拼接
pd.concat([df1, df2, df3])                  # 默认是: 行拼接.
pd.concat([df1, df2, df3], axis='rows')     # 效果同上
pd.concat([df1, df2, df3], axis=0)          # 效果同上, 0 => rows, 行,   1 => columns, 列

# 2. 演示列
pd.concat([df1, df2, df3], axis='columns')  # 列拼接
pd.concat([df1, df2, df3], axis=1)          # 效果同上
# 3. 演示 行, 列拼接时, 重置: 索引 和 列名
# 细节: 无论是行, 列拼接时, 只要忽略索引了, 都会默认用 0 ~ n来填充.
pd.concat([df1, df2, df3], axis='rows', ignore_index=True)     # 行拼接
pd.concat([df1, df2, df3], axis='columns', ignore_index=True)     # 列拼接
# 4. 验证 行拼接时, 参考: 列名
df4 = pd.DataFrame(['n1', 'n2', 'n3'], columns=['B'], index=['a', 1, 'c'])
df4
# 5. 拼接 df1 和 df4
# 行拼接, 参考: 列名
pd.concat([df1, df4], axis='rows')      # 未匹配, 用NAN填充.

# 列拼接, 参考: 索引列
pd.concat([df1, df4], axis='columns')   # 未匹配, 用NAN填充.

1.3 df对象与Series对象拼接

# 1. 创建Series对象.
s1 = pd.Series(['n1', 'n2', 'n3'])      
s1  # Series对象代表一列数据, 他没有类似于df的列名, 所以 行拼接时, 类似于: 新增了1列 
# 2. 使用concat拼接df和series对象.
pd.concat([df1, s1], axis='rows')        # 默认是: 行拼接.
pd.concat([df1, s1], axis='columns')     # 默认是: 列拼接.
# 3. 细节, 关于append()函数, 旧版本的Anaconda(例如: Anaconda2020)支持, 新版本中已经被移除掉了, 它能实现的事儿, 用concat()都能做. 
# df1.append(df2)     # 报错
pd.concat([df1, df2]) # 可以平替上述的功能

1.4 给df对象新增1列

# 方式1: df对象[列名] = 列表, 要求: 列表的长度 要和 df的行数一致.
# df1['new_col1'] = [10, 20, 30]          # 报错
df1['new_col1'] = [10, 20, 30, 40]      # 正确
df1
# 上述的代码, 加入 concat()
df5 = pd.concat([df1, df2], axis=1)       # 列拼接
df5['new_col1'] = [10, 20, 30, 40]
df5
# 方式2: df对象[列名] = Series对象, Series对象值的个数无要求.
df1['new_col2'] = pd.Series([1, 2, 3])
df1['new_col3'] = pd.Series([1, 2, 3, 4])
df1['new_col4'] = pd.Series([1, 2, 3, 4, 5])
df1

2.演示merge()函数

2.1准备动作

# 1. 创建连接对象, 关联: *.db文件.
conn = sqlite3.connect('data/chinook.db')

# 2. 从上述的文件中, 读取 歌曲表的信息.
# 参1: 要执行的SQL语句,   参2: 连接对象.
tracks_df = pd.read_sql_query('select * from tracks;', conn)
tracks_df.head()
# 3. 从上述的文件中, 读取 歌曲分类表的信息.
genres_df = pd.read_sql_query('select * from genres;', conn)
genres_df

2.2 merge()合并数据,一对一关系

  • on表示两个df合并的 关联字段, 如果一样可以直接写 on, 如果不一样, 则要写 left_on=‘左表字段名’, right_on=‘右表字段名’
  • how表示合并方式, 内连接: inner, 左连接: left, 右连接: right, 全(满)连接: outer
  • merge的格式. df1.merge(df2, on=‘关联字段’, how=‘连接方式’)
# 1. 查看 歌曲风格表的信息
genres_df
# 2. 查看 歌曲表的信息, 并从中找到 不同的音乐风格的数据.
tracks_subset_df = tracks_df.loc[[0, 62, 76, 98, 110, 193, 204, 281]]
tracks_subset_df

tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']]        # 歌曲id, 风格id, 歌曲时长(毫秒)
# 3. 合并上述两个表, 以 风格 为标准, 合并.

# 场景1: 内连接
# 参1: 要被合并的df对象. 
# 参2: on表示两个df合并的 关联字段, 如果一样可以直接写 on, 如果不一样, 则要写 left_on='左表字段名', right_on='右表字段名'
# 参3: how表示合并方式, 内连接: inner, 左连接: left, 右连接: right, 全(满)连接: outer
genres_df.merge(tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='inner')
# 场景2: 左外连接
genres_df.merge(tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='left')
# 场景3: 右外连接
genres_df.merge(tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='right')
# 场景4: 满外连接, 也叫: 全连接, 即: 它的查询结果 = 左连接 + 右连接,   即: 左表全集 + 右表全集 + 交集.
genres_df.merge(tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='outer')
# 场景5: 查看默认是哪种连接. 
genres_df.merge(tracks_subset_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId')     # 默认是: 内连接 => inner
# 场景6: 如果关联的多个df有重名的列, 则默认会加上 _x, _y这样的后缀, 来源于: suffixes字段.
genres_df.merge(tracks_subset_df[['TrackId', 'Name', 'GenreId', 'Milliseconds']], on='GenreId')     # 默认后缀: _x, _y
genres_df.merge(tracks_subset_df[['TrackId', 'Name', 'GenreId', 'Milliseconds']], on='GenreId', suffixes=('_left', '_right'))     # 默认后缀: _x, _y

2.3 merge()合并数据,一对多关系

# 1. 合并 genres(风格表) 和 tracks(歌曲表)
genres_df.merge(tracks_df[['TrackId', 'Name', 'GenreId', 'Milliseconds']], on='GenreId') 
# 需求2: 计算每种类型音乐的平均时长. 
# 1. 合并 genres(风格表) 和 tracks(歌曲表).  交集.
genre_track =  genres_df.merge(tracks_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='inner')    # 风格表.merge(歌曲表['歌曲id', '风格id', '歌曲时长毫秒']) 
# 左外连接.
genre_track =  genres_df.merge(tracks_df[['TrackId', 'GenreId', 'Milliseconds']], on='GenreId', how='left')    # 风格表.merge(歌曲表['歌曲id', '风格id', '歌曲时长毫秒']) 
genre_track
# 2. 根据 风格id分组, 计算 时长的平均值.
genre_time = genre_track.groupby(['GenreId', 'Name']).Milliseconds.mean()
genre_time
# 3.把上述的 genre_time => 秒.
pd.to_timedelta(genre_time, unit='ms').dt.floor('s')

# pd.to_timedelta(genre_time, unit='ms') 意思是: 把 genre_time 的毫秒数, 转换成 pandas.Timedelta 类型.
# dt.floor('s') 意思是: 取整, 取秒.
pd.to_timedelta(genre_time, unit='ms').dt.floor('s').sort_values()

# 总结: merge的格式.   df1.merge(df2, on='关联字段', how='连接方式')
# 细节: 1. 默认是inner.    2. 关联字段不一致, 用 left_on 和 right_on     3.两个df的字段有重名, 可以通过 suffixes 解决.

3.演示join()合并

# 1. 加载数据, 获取df对象.
stock_2016 = pd.read_csv('data/stocks_2016.csv')
stock_2017 = pd.read_csv('data/stocks_2017.csv')
stock_2018 = pd.read_csv('data/stocks_2018.csv')
stock_2016
stock_2017
stock_2018
# 2. 默认情况下, join会参考 两个df的 索引列 进行合并连接.
stock_2016.join(stock_2017, lsuffix='_2016', rsuffix='_2017')               # 默认: 左外连接
stock_2016.join(stock_2017, lsuffix='_2016', rsuffix='_2017', how='left')   # 效果同上
# 3. 设置两个df对象的 Symbol列为索引列, 再次关联.
stock_2016.set_index('Symbol')
stock_2017.set_index('Symbol')

# 设置索引列, 并关联. 
stock_2016.set_index('Symbol').join(stock_2017.set_index('Symbol'), lsuffix='_2016', rsuffix='_2017', how='left')   # 左外连接
stock_2016.set_index('Symbol').join(stock_2017.set_index('Symbol'), lsuffix='_2016', rsuffix='_2017', how='right')  # 右外连接
stock_2016.set_index('Symbol').join(stock_2017.set_index('Symbol'), lsuffix='_2016', rsuffix='_2017', how='outer')  # 满外连接
stock_2016.set_index('Symbol').join(stock_2017.set_index('Symbol'), lsuffix='_2016', rsuffix='_2017', how='inner')  # 内连接
# 4. 设置stock_2016的索引为: Symbol 和 stock_2018做关联.
stock_2016
stock_2018.set_index('Symbol')

# 拿着 stock_2016的 指定列(普通列) 和 stock_2018的 索引列 进行关联.
# 细节: on参数设定的是 函数外 df对象的 普通列
stock_2016.join(stock_2018.set_index('Symbol'), lsuffix='_left', rsuffix='_right', on='Symbol', how='outer')


总结: join() => 1. 默认是 左外连接. 2.如果两个df有重名字段, 需要手动设置后缀名. 3.默认是根据两个df的 索引列来合并的, 如果想要关联普通列, 需要通过 on 参数实现.

<think>好的,用户问的是关于ArcGIS中的缺失值的问题。首,我需要回忆一下ArcGIS处理数据时可能遇到的缺失值情况。ArcGIS主要用于地理信息系统,处理的是空间数据,比如栅格数据和矢量数据。 用户可能遇到的情况是,在使用ArcGIS分析数据时,某些区域的数据值缺失,显示为NoData或者其他标识。这时候需要了解如何处理这些缺失值,比如在栅格计算中如何排除它们,或者在统计分析时如何处理。 接下来,我需要分步骤思考。首,明确缺失值在ArcGIS中的表现形式。对于栅格数据,通常用NoData表示缺失,而矢量数据中可能是属性表中的空值或特定符号。然后,考虑用户可能的需求:他们可能想知道如何识别缺失值,如何处理这些缺失值,比如插值、删除或者填充,以及在不同工具中的处理方式。 还要注意用户可能的实际应用场景。比如,在制作地图时,缺失值会影响可视化效果;在空间分析中,缺失值可能导致结果不准确。因此,需要介绍常见的处理方法和工具,比如使用栅格计算器中的Con函数,或者插值工具来填补缺失区域。 另外,需要提醒用户注意数据源的问题,有时缺失值可能是因为数据采集的问题,需要检查原始数据是否正确。同时,不同工具对缺失值处理方式可能不同,比如某些统计工具会自动忽略NoData,而其他工具可能需要显式处理。 最后,整理这些思路,用清晰的结构回答用户,分点说明缺失值的定义、常见场景、处理方法、注意事项以及示例操作步骤,确保用户能够逐步理解和解决问题。</think>在ArcGIS中,缺失值(Missing Values)通常指数据集中未被定义或无法计算的值,其处理方式直接影响空间分析的准确性。以下是分步说明: 1. **缺失值的定义表现形式** - **栅格数据**:以`NoData`标识,表示像元无有效值。例如:$$ \text{高程值} = \text{NoData} $$ - **矢量数据**:属性表中可能为空值(NULL)或特殊标记(如-9999) 2. **常见场景** - 遥感影像云遮挡区域 - 传感器采集失败的数据点 - 数据拼接时的边缘不匹配区域 - 统计分析中的空字段 3. **处理方法工具** - **识别缺失值** - 栅格:使用`Identify`工具点击像元查看值 - 矢量:通过属性表筛选`IS NULL`查询 - **栅格数据修复** *焦点统计*:`Spatial Analyst Tools → Neighborhood → Focal Statistics` ```python # 使用Python窗口示例 arcpy.gp.FocalStatistics_sa("input.tif", "output.tif", "RECTANGLE 3 3 CELL", "MEAN", "DATA") ``` *插值法*:`Geostatistical Analyst → 地统计向导`选择克里金法 - **矢量数据处理** - 删除空记录:`Selection → Select By Attributes → "FIELD_NAME" IS NULL`后删除 - 字段计算器填充:`!FIELD_NAME! = ifelse(!FIELD_NAME!, 新值)` 4. **关键注意事项** - 工具差异性:`Zonal Statistics`会忽略NoData,而`Cell Statistics`可能将其视为0 - 投影影响:数据坐标系不匹配可能导致意外NoData - 存储格式:TIFF格式明确支持NoData,某些格式(如JPEG)不支持 5. **典型问题解决方案** **问题场景**:DEM数据出现条带状NoData **处理步骤**: 1. 使用`Con(IsNull("dem"), FocalStatistics("dem"), "dem")`进行邻近填充 2. 对边缘区域采用`Extract by Mask`裁剪 3. 通过`Topo to Raster`进行水文校正 建议在处理前始终使用`Calculate Statistics`工具获取数据分布特征,并通过`Histogram`直方图观察缺失值比例。当缺失率>15%时,应考虑数据源替代方案而非强行插值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值