python数据处理

文章讲述了在使用Python解压gz文件时遇到的问题,包括错误信息和尝试的解决办法,如升级Python版本、更换API等。同时,讨论了在处理大量数据时,如何通过分片处理、多线程和循环优化来提高数据处理速度,特别是涉及从大量表中提取用户数据的场景。还提到了文件命名过长导致的问题及其影响。
摘要由CSDN通过智能技术生成

今天,跑数据,很无聊

遇到问题:

  1. .gz压缩文件解压不了,太抽象
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import gzip
 
def un_gz(file_name):
    
    # 获取文件的名称,去掉后缀名
    f_name = file_name.replace(".gz", "")
    # 开始解压
    g_file = gzip.GzipFile(file_name)
    #读取解压后的文件,并写入去掉后缀名的同名文件(即得到解压后的文件)
    open(f_name, "wb+").write(g_file.read())
    g_file.close()
    
un_gz('D:\\python36\\config.gz')

报错:
解压.gz文件报错信息
gzip源码
大概意思就是,文件格式不对,解压的魔数对不上;太抽象了
网上检索了一堆信息,说是压缩文件坏掉了,可是我又可以手动解压,真的抽象
gzip报错原因
尝试办法:

  • 升级python版本,从3.7 -> 3.9;因为我看了gzip api文档,不同版本的api有所不同;但是没用
    gzip.open()
    gzip.GzipFile()

  • 更换api,(1)直接用python调用os ungzip命令(2)直接pd.read_csv(‘data.csv.gz’, compression=‘gzip’)等等;但是没用,还是报错,属实抽象;如何使用Python解压gz文件
    最后,用了最原始的方法:
    select all,extract files;简单粗暴,没有办法的办法

  1. 数据量太大,怎么优化跑得快一点
    问题描述,从一张表里面读取user_id,根据id去查另外12种类型的数据(每种数据有多张表,共1000+张表),获取每个用户的相应数据写到一个文件夹里

这里核心来说,应该用了几个手段来处理

  • 分片处理
  • 多线程
  • 循环优化

首先,读取user_id,从一张csv里面抽取了100万+的user_id;这步的话直接串行处理了,因为是一次性的前置操作;大概花了几分钟;如果要优化的话,可以多线程分块读取csv处理,最后再合并数组也行;

第二步是关键,遍历,是先遍历数据表,还是先遍历user_id

for user in user_list:
	for table in table_list:

时间复杂度:1000的1000000次方,可以获取部分用户的完整数据;一张表近5M,频繁读取会很耗时,所以应该优先考虑,每张表只用读取一次就完成相应的操作

for table in table_list:
	for user in user_list

时间复杂度:1000000的1000次方,每张表只需要读入内存一次;
最终,我结合了这两个方法
(1)先对user_list进行分片

def list_of_groups(init_list, childern_list_len):
    '''
    init_list为初始化的列表,childern_list_len初始化列表中的几个数据组成一个小列表
    :param init_list:
    :param childern_list_len:
    :return:
    '''
    list_of_group = zip(*(iter(init_list),) * childern_list_len)
    end_list = [list(i) for i in list_of_group]
    count = len(init_list) % childern_list_len
    end_list.append(init_list[-count:]) if count != 0 else end_list
    return end_list
for table in table_list:
	group_lists = list_of_groups(user_list, SIZE)
	for index, children_user_list in enumerate(group_lists):
		for user_id in children_user_list:
		## extract data

这样实现的好处是,我们在保证每张表只读取一次的同时,也能分批次获取部分用户的完整数据;这部分数据可以先用起来;

上面的实现嵌套了三层循环,那么我们可以进一步考虑用多线程来解决问题,因为任务是cpu密集型,所以线程就开CPU core个就好啦;Windows下查看电脑的CPU个数,核心数,线程数
也可以在跑的时候看看cpu和内存的负载,两者都能拉到50%以上吧应该,充分利用起来(但是cpu负载过高会不会是在频繁切换线程?),线程数设置真是一门学问啊,还有超线程技术呢(超线程就是一个物理核模拟出多个逻辑核)

我们还是优先考虑获取小批次用户的完整数据

pool = ThreadPoolExecutor(max_workers=CPU_CORE)
for table in table_list:
	group_lists = list_of_groups(user_list, SIZE)
	for index, children_user_list in enumerate(group_lists):
    	pool.submit(self.action, user_id)  # pool.submit是有返回值的

其它

python创建二维数组

_list = [[] for _ in range(length)]

pandas操作多列

            df.apply(lambda row: func(row['key1'], row['key2'], other_params), axis=1)

pandas函数式筛选特定的行数据

df合并,见Pandas - Merge 函数
merge()每次只能合并两张表,但是粒度更细,concat()可以一次性合并多张表,但是粒度比较粗
我的需求:根据指定的key合并多张表,所以我用的是merge()和list作为一个队列进行操作

    while len(tables) > 1:
        tables.append(pd.merge(tables.pop(), tables.pop(), on=['key1', 'key2'], how='outer'))
    df = tables.pop()

    df = pd.DataFrame(columns=columns_name)
    res = pd.concat([df, *df_list]) # df_list包含多个df, *df_list解构

合并多张表如果有列名相同,rename就好啦
df修改列名

df = df.rename(columns=lambda x: rename_columns(x, column_suffix))
# 或者直接指定
df = df.rename(columns={'a':'A'})

def rename_columns(x, column_suffix):
    print(x)
    if x == 'key1':
        return x
    if x == 'key2':
        return x
    return x + '(' + column_suffix + ')'

df删除空值、冗余

df = pd.read_csv(os.path.join(root, file), low_memory=False).dropna(axis=1, how='all').drop_duplicates()

drop_duplicates()默认保留第一项
dropna(axis=1, how=‘all’) 列全空删除该列

玄学问题

file_name = poor_quality_event[0] + '_' + poor_quality_event[3] + '_' + self.table_prefix_list[
    index] + '.csv'
output_path = os.path.join(self.output_path, poor_quality_event[0])
if self.mkdir(output_path):
    res.to_csv(os.path.join(output_path, file_name), index=False, columns=columns_name)

今天在写csv的时候遇到了一个问题,现象如下:
(1)我先mkdir再to_csv,理论上写文件的时候,文件夹肯定是存在,但是在写文件的时候报错文件不存在;而且这个现在仅针对某一个名字比较长的文件夹[Errno 2] No such file or directory: 这个报错很坑,它没有告诉你是因为创建失败才没有的,你可以试着创建一个名字很长的csv,它就是报这个错,但是它不会跟你说是因为名字过长创建失败。
(2)csv在pycharm可以打开,但是用Microsoft打开报错;
经过我苦心孤诣,终于搞明白了,是命名过长导致的;

res.to_csv(os.path.join(output_path, file_name), index=False, columns=columns_name)

这一步创建了一个名字过长的csv失败了,导致写入的时候报错不存在
对于(2),应该是pycharm和Microsoft打开csv的方式有所区别,所以Microsoft打开报错
综上,是由于我文件夹名字本来就比较长,然后我还基于文件夹生成文件名,就更长,导致出现异常
总结就是,对于Microsoft的一些文件,不要命名过长。
而且我在pycharm重命名文件夹抛出java.io异常,说明pycharm可能是基于java实现的hhh
打开文件失败

pandas

取特定行

df.iloc[index]

Pandas DataFrame切片
pandas读取行和列

pandas遍历

for idx, item in df.iterrows():

要注意这里的idx对应的是df里面的idx不一定是从0开始的

总结

非专业数据处理,对数据不够敏感,很多时候都在踩坑,优化,一点点进步

  • 不同时间粒度的同类型表,合并数据表时根据需求取某一时间粒度的就好
  • 合并数据表的关键在于对齐主键,这个需要熟悉业务,或者至少清楚表与表之间键的关系;
  • 这种数据表通常包含标识+数值,找主键就是从标识里面去筛
  • 这个数据表的工作是很繁琐的,但是在业务中又很频繁,通常会遇到那些大宽表,取数,合表hhh

追加,写代码的时候遇到的一个小bug
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值