7-Python数据处理-小记
本文涉及的内容有:
①
numpy
和pandas
的简单应用②
python
排列组合③
python
进度条的简单使用
1.1本文背景
本文的需求用以下示例说明。
- 原始数据如下图所示,多行多列,每行的列数不一定相同
- 针对每一行,按照排列组合的方式,将该行转行成多行两列的数据。例如上图中的第2行为
1,2,3
,所有的组合情况为(1,2),(1,3),(2,3)
。则转换之后的数据就是下图所示的样子。
- 每一行都处理成上图的样子,合在一起,在第一行再加一个标签行,就得到了最终的结果。
1.2关于CSV格式
原始数据格式是.xlsx
格式,为了方便,我先转换成了.csv
格式。发现:
- 原始数据有多列,每一行的列数不固定,有多有少
- 转换成
.csv
格式(用逗号分隔)之后,每一行的列数被固定下来,统一长度为最大列数 - 转换之后的数据如下图所示。
1.3计算每一行数据的有效长度
由1.2
可知,用python
读取进来的csv
格式的数据中,有些列的元素是空的,而我们需要处理的数据是每一行的非空元素。所以需要计算每一行的有效数据长度。因此对于整个数据data
中的每一行数据data_item
,有如下步骤(非空元素只会从左边开始出现,并且是连续的):
- 获取所有空值即
''
的下标位置,这些下标位置存放在一个数组length
当中 - 如果
length
非空(说明这一行的有效元素的个数小于最大列数),则获取最后一个有效元素的下一个位置的下标位置position_last
- 如果
length
空(说明原数据有效长度等于最大列数),同样的,获取最后一个有效元素的下一个位置的下标位置 - 需要注意的是,如果
position_last==1
(说明这一行数据只有一列),需要剔除掉,即不需要进行之后的处理,直接处理下一行 - 这一过程的代码如下所示。
# 1.1计算每一行数据的有效长度
length = np.argwhere(data_item == '') # 返回所有满足条件的元素的下标位置
# print("length:", length)
if len(length) > 0:
# 有效长度小于最大列数
# 获取最后一个有效元素的下一个位置的下标位置
position_last = length[0][0]
# print(position_last)
else:
# 说明原数据有效长度等于最大列数
# 获取最后一个有效元素的下一个位置的下标位置
position_last = len(data_item)
# print("position_last:", position_last)
if position_last == 1:
# 说明这一行数据只有一列,需要剔除掉,即不需要进行之后的处理,直接跳过
continue
1.4计算所有的组合情况
- 这里用到了
combinations
函数。第一个参数是一个可迭代对象,第二个参数是每个组合的元素个数。使用list()
函数将combinations
函数的返回值变成列表,列表的每个元素是一个二元组。 1.3
也说了,有效元素只会出现在每一行的左边,因此接下来需要对左边的元素的可能组合情况进行计算;- 这里选择对元素的下标进行组合计算,之后再通过下标进行遍历即可。
- 计算组合的代码如下。
from itertools import combinations
# 1.2计算所有的组合情况
combinations_list = list(combinations(np.arange(0, position_last), 2))
# print("combinations_list:", combinations_list)
- 遍历刚刚计算得到的二元组列表,即可得到这一行数据的所有组合情况
# 1.3遍历所有组合情况,取得对应的两列数据
for combination in combinations_list:
final_data.append([data_item[combination[0]], data_item[combination[1]]])
- 所有数据处理完成之后,得到的
final_data
是一个列表,其元素是一个二元列表,在输出到文件之前还需要进行一些处理。
1.5转换成DataFrame
- 为了方便写入文件,选择将
final_data
列表转换成DataFrame
格式。转换方式如下所示。
# 2.1定义列名
names = ['id1', 'id2']
# 2.2转换成DataFrame格式,方便写入csv文件
pd_data = pd.DataFrame(columns=names, data=final_data)
# print('pd_data:\n', pd_data)
- 由于数据处理的需要,我发现当前处理好的数据中存在有些航,其两列的元素值完全一样,对于我是没有用的,需要将这样的行删除掉。
- 首先获取需要删除的行的行索引值
- 再使用
drop
函数一起删除掉 - 这一过程还是用了进度条功能(导入
tqdm
包,并将迭代对象放入tqdm()
函数中即可) - 代码如下所示。
print("正在删除不符合条件的数据...")
time.sleep(2)
# 2.3由于数据中存在某些行,其两列元素是完全一样的,因此增加了剔除这样的行的步骤
index_list = [] # 存放需要剔除掉的数据所在的行号
with tqdm(total=pd_data.shape[0]) as pbar:
for index, row in pd_data.iterrows():
# 按行遍历,将DataFrame的每一行迭代为(index, Series)
# 对于每一行,通过列名name访问对应的元素
if row['id1'] == row['id2']:
# 两列元素是完全一样的
index_list.append(index)
pbar.update(1)
# print("index_list:", index_list)
pd_data = pd_data.drop(index=index_list)
# print('pd_data:\n', pd_data)
- 最后写入文件即可
# 2.4写入文件
print("正在写入文件...")
pd_data.to_csv("data_processed.csv", encoding='utf8', index=False) # 不保留行索引
1.6完整代码
数据可参照本文讲述内容自行设置。
import numpy as np
from itertools import combinations
from tqdm import tqdm
import pandas as pd
import time
data = np.loadtxt("data.csv", delimiter=',', encoding='utf-8', dtype=str)
# print(data)
final_data = [] # 存放处理之后的数据
print("正在分隔数据...")
# 1、遍历每一行数据
for data_item in tqdm(data):
position_last = 0
combinations_list = []
# print("data_item:", data_item)
# print(type(data_item))
# 1.1计算每一行数据的有效长度
length = np.argwhere(data_item == '') # 返回所有满足条件的元素的下标位置
# print("length:", length)
if len(length) > 0:
# 有效长度小于最大列数
# 获取最后一个有效元素的下一个位置的下标位置
position_last = length[0][0]
# print(position_last)
else:
# 说明原数据有效长度等于最大列数
# 获取最后一个有效元素的下一个位置的下标位置
position_last = len(data_item)
# print("position_last:", position_last)
if position_last == 1:
# 说明这一行数据只有一列,需要剔除掉,即不需要进行之后的处理,直接跳过
continue
# 1.2计算所有的组合情况
combinations_list = list(combinations(np.arange(0, position_last), 2))
# print("combinations_list:", combinations_list)
# 1.3遍历所有组合情况,取得对应的两列数据
for combination in combinations_list:
final_data.append([data_item[combination[0]], data_item[combination[1]]])
time.sleep(2)
print("正在转换格式...")
# print("final_data:", final_data)
# print(type(final_data))
# 2、将数据写入csv文件
# 2.1定义列名
names = ['id1', 'id2']
# 2.2转换成DataFrame格式,方便写入csv文件
pd_data = pd.DataFrame(columns=names, data=final_data)
# print('pd_data:\n', pd_data)
print("正在删除不符合条件的数据...")
time.sleep(2)
# 2.3由于数据中存在某些行,其两列元素是完全一样的,因此增加了剔除这样的行的步骤
index_list = [] # 存放需要剔除掉的数据所在的行号
with tqdm(total=pd_data.shape[0]) as pbar:
for index, row in pd_data.iterrows():
# 按行遍历,将DataFrame的每一行迭代为(index, Series)
# 对于每一行,通过列名name访问对应的元素
if row['id1'] == row['id2']:
# 两列元素是完全一样的
index_list.append(index)
pbar.update(1)
# print("index_list:", index_list)
pd_data = pd_data.drop(index=index_list)
# print('pd_data:\n', pd_data)
# 2.4写入文件
print("正在写入文件...")
pd_data.to_csv("data_processed.csv", encoding='utf8', index=False) # 不保留行索引
_list:", index_list)
pd_data = pd_data.drop(index=index_list)
# print('pd_data:\n', pd_data)
# 2.4写入文件
print("正在写入文件...")
pd_data.to_csv("data_processed.csv", encoding='utf8', index=False) # 不保留行索引