使用pandas处理数据的一些总结

本文介绍了如何使用Pandas对DataFrame进行数据清洗,包括替换特殊符号、清除空格、去重操作,并关注了CSV文件中的换行符处理以及如何在Hive中正确表示NULL值。

1、替换换行符等特殊符号

df = df.replace({None: "", np.nan: "", "\t": "", "\n": "", "\x08": ""}, regex=True)

2、清除DataFrame中所有数据的左右空格,字符串中间空格不会清除,类似于字符串的strip()方法,如果是Serise类型可以使用df.str.strip()方法

df = df.applymap(lambda x: " ".join(x.split()) if isinstance(x, str) else x)

3、dataframe数据去重

dataframe数据样本:

import pandas as pd
df = pd.DataFrame({'name':['苹果','梨','草莓','苹果'], 'price':[7,8,9,8], 'cnt':[3,4,5,4]})

   name	cnt	price
0	苹果	 3	7
1	 梨	 4	 8
2	草莓	 5	9
3	苹果	 6	8

3.1、查看dataframe的重复数据

a = df.groupby('price').count()>1
price = a[a['cnt'] == True].index
repeat_df = df[df['price'].isin(price)]

3.2、duplicated()方法判断

3.2.1、 判断dataframe数据某列是否重复

flag = df.price.duplicated()

0    False
1    False
2    False
3     True
Name: price, dtype: bool

flag.any()结果为True  (any等于对flag or判断)
flag.all()结果为False  (all等于对flag and判断)

3.2.2、判断dataframe数据整行是否重复

flag = df.duplicated()
判断方法同1

3.2.3、判断dataframe数据多列数据是否重复(多列组合查)

df.duplicated(subset = ['price','cnt'])
判断方法同1

3.3、 drop_duplicats()方法去重

对dataframe数据数据去重

DataFrame.drop_duplicates(subset=None, keep='first', inplace=False)

示例:
df.drop_duplicats(subset = ['price','cnt'],keep='last',inplace=True)

drop_duplicats参数说明:
  参数subset
    subset用来指定特定的列,默认所有列
  参数keep
    keep可以为first和last,表示是选择最前一项还是最后一项保留,默认first
  参数inplace
    inplace是直接在原来数据上修改还是保留一个副本,默认为False

4、 文件中含有换行符时,需要保留换行符

import csv

import pandas as pd
#
data = {'col1': ['Hello\nWorld', 'Line\nbreak"'], 'col2': [1, 2]}
df = pd.DataFrame(data)
print(df)
df.to_csv('data.csv', line_terminator='\n', quoting=csv.QUOTE_ALL, index=False, header=False)

df = pd.read_csv('data.csv', header=None)
print(df)

运行结果:

5、当csv文件入hive时,遇到空值需要在hive中显示为null值

CREATE TABLE my_table
(
    id    int comment '编号',
    name  string comment '名称',
    money decimal(10, 2) comment '金钱'
)
    ROW FORMAT DELIMITED
        FIELDS TERMINATED BY ','
        LINES TERMINATED BY '\n'
        NULL DEFINED AS ''
    STORED  AS TEXTFILE
    TBLPROPERTIES ("serialization.null.format"='');

NULL DEFINED AS ''将空字符串视为NULL值,'serialization.null.format'=''则用于指定NULL值在数据文件中的表示方式。通过这种方式,我们可以同时使用NULL DEFINED AS ''和serialization.null.format=''来将NULL值和空字符串表示在Hive中的一致性。
也可以修改已存在的表,如下 

alter table my_table set serdeproperties('serialization.null.format' = '');

6、pandas处理xlsx文件出现科学计数法数据

源数据长这样

 作者查阅了网上资料使用converters或dtype

  1. dtype 用于指定列的数据类型
  2. converter 用于应用自定义转换函数
  3. 转换器优先级更高,因为它可以执行更复杂的操作
  4. 如果同时指定,Pandas 会忽略 dtype 以避免冲突
def pre_script_func(remote_file, transform_file_path, dest_tb_columns, transform_file_seperator):
    file_tb = pd.read_excel(remote_file,
                            sheet_name=2,
                            engine="openpyxl",
                            header=0,
                            names=dest_tb_columns,
                            usecols=[_ for _ in range(len(dest_tb_columns))],
                            converters={_: str for _ in dest_tb_columns},
                            # dtype={i: str for i in range(len(dest_tb_columns))}
                            )
    file_tb.to_json(transform_file_path, orient="records", lines=True, force_ascii=False)
    return transform_file_path

但返回的数据且不是我想要的数据,如果你的数据返回正常那么恭喜你可以不用往下看了。

那么数据为啥没有转化成功呢?

好吧,不卖关子了,其实是因为源数据有可能有多种类型的数据同时存在,那么我们使用apply方法来进行特殊处理吧

def pre_script_func(remote_file, transform_file_path, dest_tb_columns, transform_file_seperator):
    file_tb = pd.read_excel(remote_file,
                            sheet_name=2,
                            engine="openpyxl",
                            header=0,
                            names=dest_tb_columns,
                            usecols=[_ for _ in range(len(dest_tb_columns))],
                            converters={_: str for _ in dest_tb_columns},
                            # dtype={i: str for i in range(len(dest_tb_columns))}
                            )
    def convert_value(value):
        """处理混合类型值"""
        if isinstance(value, float):
            # 处理浮点数(科学计数法)
            return f"{value:.0f}"  # 转换为整数字符串
        elif isinstance(value, int):
            # 处理整数
            return str(value)
        elif isinstance(value, str):
            # 处理字符串
            if 'e+' in value or 'e-' in value:
                # 处理科学计数法字符串
                try:
                    return f"{float(value):.0f}"
                except:
                    return value
            else:
                return value
        else:
            return str(value)
    file_tb[['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']] = file_tb[['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']].applymap(convert_value)
    #
    # # 替换DataFrame中的空字符串为Nan
    # # file_tb.replace("", np.nan, inplace=True)
    file_tb.to_json(transform_file_path, orient="records", lines=True, force_ascii=False)
    return transform_file_path

这回好像数据都是正常的了

别高兴太早,我们来看看源数据为243120000003649000,返回的数据却是243120000003648992;什么原因呢?精度损失了?

那是因为使用 f"{float(value):.0f}" 确实会导致大整数精度损失,因为 Python 的浮点数(IEEE 754 双精度)只能精确表示最多约 15-17 位的整数。对于超过这个位数的整数(如 243120000003649000),转换为浮点数再转回整数会导致精度损失。

精度损失的原因

  1. 浮点数精度限制

    • IEEE 754 双精度浮点数有 53 位尾数
    • 只能精确表示绝对值小于 2⁵³ (约 9e15) 的整数
    • 243120000003649000 (18位) > 9e15 (16位),超出精确表示范围
  2. 转换过程

    original = 243120000003649000
    as_float = float(original)  # 变为 243120000003649000.0 (但实际存储为近似值)
    back_to_int = int(as_float)  # 变为 243120000003648992 (精度损失)

完整解决方案:避免浮点数转换,代码修改后 

def pre_script_func(remote_file, transform_file_path, dest_tb_columns, transform_file_seperator):
    file_tb = pd.read_excel(remote_file,
                            sheet_name=2,
                            engine="openpyxl",
                            header=0,
                            names=dest_tb_columns,
                            usecols=[_ for _ in range(len(dest_tb_columns))],
                            converters={_: str for _ in dest_tb_columns},
                            # dtype={i: str for i in range(len(dest_tb_columns))}
                            )

    def convert_value(value):
        """处理混合类型值,避免精度损失"""
        # 检查是否为 NaN
        if isinstance(value, float) and math.isnan(value):
            return ""  # 或者返回 None 或其他占位符

        if isinstance(value, float):
            # 对于大浮点数,直接格式化为字符串
            if abs(value) > 1e15:
                # 使用字符串格式化避免浮点转换
                return format(value, '.0f')
            # 对于小浮点数,检查是否为整数
            if value.is_integer():
                return str(int(value))
            return str(value)  # 保留小数部分

        elif isinstance(value, int):
            return str(value)

        elif isinstance(value, str):
            # 使用正则表达式精确匹配科学计数法
            sci_match = re.match(r'^([-+]?\d*\.?\d+)[eE]([-+]?\d+)$', value)
            if sci_match:
                # 直接处理科学计数法字符串
                try:
                    # 提取基数和指数
                    base = sci_match.group(1)
                    exp = int(sci_match.group(2))

                    # 处理底数中的小数点
                    if '.' in base:
                        integer_part, fractional_part = base.split('.')
                    else:
                        integer_part = base
                        fractional_part = ''

                    # 计算需要移动的小数点位数
                    move = exp - len(fractional_part) if exp >= 0 else exp

                    if move > 0:
                        # 正指数:在末尾补零
                        return integer_part + fractional_part + '0' * move
                    elif move < 0:
                        # 负指数:在整数部分插入小数点
                        pos = len(integer_part) + move
                        if pos > 0:
                            return integer_part[:pos] + '.' + integer_part[pos:] + fractional_part
                        else:
                            return '0.' + '0' * (-pos) + integer_part + fractional_part
                    else:
                        return integer_part + fractional_part
                except Exception as e:
                    print(f"处理科学计数法时出错: {e}, 值: {value}")
                    return value
            else:
                return value
        else:
            return str(value)
    file_tb[['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']] = file_tb[['col1', 'col2', 'col3', 'col4', 'col5', 'col6', 'col7']].applymap(convert_value)
    #
    # # 替换DataFrame中的空字符串为Nan
    # # file_tb.replace("", np.nan, inplace=True)
    file_tb.to_json(transform_file_path, orient="records", lines=True, force_ascii=False)
    return transform_file_path

数据正常了 

7、pandas处理所有列非NaN值的数据类型转化为字符串

使用 mask 条件替换

通过布尔掩码选择性转换:

file_tb = file_tb.mask(file_tb.notna(), file_tb.astype(str))

逻辑

其他场景

  • file_tb.notna():生成布尔掩码(True 为非 NaN 位置)。
  • .mask(cond, other):对 cond=True 的位置替换为 other(此处为字符串化后的值)。
方法适用场景代码示例
applymap小规模数据,逐元素控制df.applymap(lambda x: str(x) if pd.notna(x) else x)
列级循环中等规模数据,平衡性能与可读性for col in df: df[col] = df[col].apply(lambda x: str(x) if pd.notna(x) else x)
mask向量化操作,性能最优df.mask(df.notna(), df.astype(str))
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值