Python小案例(一)非结构化文本数据处理

Python小案例(一)非结构化文本数据处理

日常业务需求中,仅凭SQL一招鲜是没法吃遍天的,这个时候就需要更为强大的Python进行支持了。这个系列主要分享一些Python小案例,都是根据笔者日常工作需求抽离总结的,如有雷同,纯属巧合~

这一期,主要是利用python处理非结构化文本数据。而且每个小案例可能隐藏着一些使用的Pandas技巧.

嵌套json展开

隐藏知识点:函数递归

# ⚠️注意:用`json.loads`处理json型字符串时,键值应用双引号,外围用单引号。否则会转换失败,这里只是简单处理,所以采用`eval`函数避免转换错误。
# 随机构造一个json型的字符串
s = "{'A':111,'B':{'2':'b','1':{'a':23,'b':34,'c':{'HH':'good','hhh':[3,2,'q']}},\
    '3':[1,2,3]},'C':['a',34,{'mm':567,'gg':678}]}"
# 开发半展开json函数
def json_half_flat(dic):
    '''
    半展开json,只对字典类型展开
    dic:字典
    
    return:展开后的字典
    '''
    init_dic = {}
    keys = list(dic.keys())
    for i in keys:
        init_dic_value = dic[i]
        if type(init_dic_value) == dict:
            init_dic_value = json_half_flat(init_dic_value)
            init_dic_keys = list(init_dic_value.keys())
            for j in init_dic_keys:
                name = str(i) + '_' + str(j)
                init_dic[name] = init_dic_value[j]
        else:
            init_dic[i] = init_dic_value
    return init_dic

# 开发全展开json函数
def json_flat(dic):
    '''
    全展开json,对字典、列表均进行展开
    dic:字典
    
    return:展开后的字典
    '''
    init_dic = {}
    keys = list(dic.keys())
    for i in keys:
        init_dic_value = dic[i]
        if type(init_dic_value) == dict:
            init_dic_value = json_flat(init_dic_value)
            init_dic_keys = list(init_dic_value.keys())
            for j in init_dic_keys:
                name = str(i) + '_' + str(j)
                init_dic[name] = init_dic_value[j]
        elif type(init_dic_value) == list:
            length = len(init_dic_value)
            for j in range(length):
                name = str(i) + '_' + str(j)
                init_dic[name] = init_dic_value[j]
        else:
            init_dic[i] = init_dic_value
    return init_dic
s2j = eval(s)
json_half_flat(s2j) # 下方输出结果可以发现半展开json函数对列表或者列表内的json不做处理
{'A': 111,
 'B_2': 'b',
 'B_1_a': 23,
 'B_1_b': 34,
 'B_1_c_HH': 'good',
 'B_1_c_hhh': [3, 2, 'q'],
 'B_3': [1, 2, 3],
 'C': ['a', 34, {'mm': 567, 'gg': 678}]}
s2j = eval(s)
json_flat(s2j) # 下方输出结果可以发现全展开json会对所有的json和列表均进行展开
{'A': 111,
 'B_2': 'b',
 'B_1_a': 23,
 'B_1_b': 34,
 'B_1_c_HH': 'good',
 'B_1_c_hhh_0': 3,
 'B_1_c_hhh_1': 2,
 'B_1_c_hhh_2': 'q',
 'B_3_0': 1,
 'B_3_1': 2,
 'B_3_2': 3,
 'C_0': 'a',
 'C_1': 34,
 'C_2': {'mm': 567, 'gg': 678}}

提取地址

这里介绍可用于处理中文地址的cpca库,甚至还具有自动补全地址省市的功能。

隐藏知识点:列表列拆分为多列

pip install cpca
import pandas as pd
import numpy as np
import cpca
# 构造地址数据df
df_address = pd.DataFrame(
    {'id':[1,2,3],
       'address':["徐汇区虹漕路461号58号楼5楼", "泉州市洛江区万安塘西工业区", "北京朝阳区北苑华贸城"]})

df_address
id address
0 1 徐汇区虹漕路461号58号楼5楼
1 2 泉州市洛江区万安塘西工业区
2 3 北京朝阳区北苑华贸城
def get_address(location_str):
    '''
    提取字符串的地址信息
    location_str:地址字符串
    
    return:省、市、区、地址、邮编
    '''
    location_str=[location_str]
    df = cpca.transform(location_str)
    try:
            p=df['省'].values[0]
            c=df['市'].values[0]
            d=df['区'].values[0]
            ad=df['地址'].values[0]
            adcode=df['adcode'].values[0]
    except:
            pass
    return p,c,d,ad,adcode
df_address['local'] = df_address['address'].apply(get_address)
df_address[['province', 'city', 'district', 'address', 'adcode']] = df_address['local'].apply(pd.Series) # 列表拆分为多列
df_address
id address local province city district adcode
0 1 虹漕路461号58号楼5楼 [上海市, 市辖区, 徐汇区, 虹漕路461号58号楼5楼, 310104] 上海市 市辖区 徐汇区 310104
1 2 万安塘西工业区 [福建省, 泉州市, 洛江区, 万安塘西工业区, 350504] 福建省 泉州市 洛江区 350504
2 3 北苑华贸城 [北京市, 市辖区, 朝阳区, 北苑华贸城, 110105] 北京市 市辖区 朝阳区 110105

提取url

这里通过urlextract库进行url提取,并通过正则过滤非图片url

隐藏知识点:列转多行

# !pip install urlextract
import pandas as pd
import numpy as np
import re
# 构造地址数据df
df_pic = pd.DataFrame(
    {'id':[1,2],
       'content':[
           'src=\"https://sm-wick-question.oss-cn-zhangjiakou.aliyuncs.com/QuestionAnswerImage/64809-1276-question.jpg\">
", "answer": "
"' ,'"avatar_url": "https://image.uc.cn/s/wemedia/s/upload/2021/5850c345e69483fd27b2622e9216273f.png", "description": "优秀青年教师", "fans_count": 0, "columns_count": 71, "courses_count": 0, "page_url": "https://course.myquark.cn/page/teacher?entrance=paisou_kgcard&page_name=page_a2s0k_course_learning&md_frm=teacher_area&uc_biz_str=qk_enable_gesture%3Afalse%7COPT%3ABACK_BTN_STYLE%400%7COPT%3AKEEP_SCREEN_ON%401%7COPT%3AW_PAGE_REFRESH%400%7COPT%3AS_BAR_BG_COLOR%40FFFFFF%7Cnull#/!{%22teacher_id%22:%22c099794880e34395b5bb146a1a04da99%22}", "cover_url": "http://image.uc.cn/s/wemedia/s/upload/2021/13078b26626a526e577585f6fc93430a.png"' ]}) df_pic
id content
0 1 src="https://sm-wick-question.oss-cn-zhangjiak...
1 2 "avatar_url": "https://image.uc.cn/s/wemedia/s...
# 提取url
def get_urls(s):
    '''提取字符串的url
    
    s:字符串
    
    return:url列表
    '''
    from urlextract import URLExtract
    
    extractor = URLExtract()
    urls = extractor.find_urls(s)
    
    return urls

# 列转多行
def split_row(df, column):
    """ 拆分成多行

    df: 原始数据
    column: 拆分的列名

    return: df
    """
    row_len = list(map(len, df[column].values))
    rows = []
    for i in df.columns:
        if i == column:
            row = np.concatenate(df[i].values)
        else:
            row = np.repeat(df[i].values, row_len)
        rows.append(row)
    return pd.DataFrame(np.dstack(tuple(rows))[0], columns=df.columns)
# 提取urls
df_pic['urls'] = df_pic['content'].map(lambda x: get_urls(x))

# 列转多行
df_pic_result = split_row(df_pic, column='urls')

# 提取图片url
df_pic_result['pic'] = df_pic_result['urls'].map(lambda x: re.search('jpg|png', x, re.IGNORECASE))
df_pic_result = df_pic_result.dropna(subset=['pic']) # 删除没正则匹配到图片的数据

# 剔除尾部反斜杠、截取到jpg|png
df_pic_result['urls_new'] = df_pic_result['urls'].map(lambda x: re.search('http(.+?)(png|jpg)', x.rstrip('\\'), re.IGNORECASE).group(0))

# 展示结果
df_pic_result[['id', 'urls_new']]
id urls_new
0 1 https://sm-wick-question.oss-cn-zhangjiakou.aliyuncs.com/QuestionAnswerImage/64809-1276-question.jpg
1 1 https://sm-wick-question.oss-cn-zhangjiakou.aliyuncs.com/QuestionAnswerImage/64809-1276-answer.jpg
2 2 https://image.uc.cn/s/wemedia/s/upload/2021/5850c345e69483fd27b2622e9216273f.png
4 2 http://image.uc.cn/s/wemedia/s/upload/2021/13078b26626a526e577585f6fc93430a.png

统计中文

利用正则过滤非中文,通过Python的Counter进行统计。

import re
from collections import Counter

# 构造数据
s = ['asdk;*教师oq年教w%8青年教qw优秀asd;青年教师asd','教w%8青年教qw优秀a']

# 统计所有汉字
cn = Counter()
for i in s:
    s_ch = re.sub('[^\u4e00-\u9fa5]+', '', i) # 剔除非中文
    for j in s_ch:
        if j=='':
            continue
        cn[j]=cn[j]+1
            
# 输出结果
print('统计结果:',list(cn.items())) # 查看统计结果
print('统计结果Top2:',cn.most_common(2)) # 查看次数最高的2个汉字
统计结果: [('教', 6), ('师', 2), ('年', 4), ('青', 3), ('优', 2), ('秀', 2)]
统计结果Top2: [('教', 6), ('年', 4)]

Hive也能完成上述任务,主要我在当时没想到split(str,‘’)可以进行分隔

-- Hive统计中文
with temp as
    (
        select 1 as id, 'asdk;*教师oq年教w%8青年教qw优秀asd;青年教师asd' as content
        union all
        select 2 as id, '教w%8青年教qw优秀a' as content
    )

select
    ch
    ,count(1) as cnt
from
    (
        select
            id
            ,ch
        from
            (
                select
                    id
                    ,regexp_replace(content,'[^\\u4e00-\\u9fa5]', '') as s_ch
                from
                    temp
            )a 
        lateral view explode(split(s_ch,'')) t as ch
    )a 
where
    ch != ''
group by 
    ch

自定义json

背景:将汉字释义按照指定规则生成对应的json提供给研发。这个案例的可扩展性一般,主要分享如何用Ptyhon灵活处理复杂的数据需求。

隐藏知识点:df.at[index, col]按照索引更新指定列的数值

import pandas as pd
import json
import re
df_sj = pd.DataFrame(
    {'id':[1,2],
        'char':['百','葡'],
        'paraphrase':["1.数词,十个十。表示众多或所有的:百花齐放,百家争鸣|百战百胜。\n2.法定计量单位中十进倍数单位词头之一,表示10²,符号h。",
                  "[葡萄]落叶藤本植物。叶子呈掌状分裂,圆锥花序,茎有卷须,可缠绕其他物体,开淡黄色小花。果实为椭圆形或圆形,多汁,味酸甜,可食用或酿酒。"]})
for index, row in df_sj.iterrows():
    paraphrase = row['paraphrase']
    list_init=[]
    for s in paraphrase.split('\n'):
        dict_init={}
        s=re.sub(r"(\d+\.)", "", s)
        if ':' in s:
            dict_init['value']=re.findall(r"(.*?):", s)[0]
            dict_init['example']=re.findall(r":(.*)", s)[0].split('|')
        else:
            dict_init['value']=s
            dict_init['example']=[]
        list_init.append(dict_init)
    df_sj.at[index, 'paraphrase_json']=json.dumps(list_init,ensure_ascii=False) # 根据索引修改列值
    
df_sj
id char paraphrase paraphrase_json
0 1 1.数词,十个十。表示众多或所有的:百花齐放,百家争鸣|百战百胜。\n2.法定计量单位中十进倍数单位词头之一,表示10²,符号h。 [{"value": "数词,十个十。表示众多或所有的", "example": ["百花齐放,百家争鸣", "百战百胜。"]}, {"value": "法定计量单位中十进倍数单位词头之一,表示10²,符号h。", "example": []}]
1 2 [葡萄]落叶藤本植物。叶子呈掌状分裂,圆锥花序,茎有卷须,可缠绕其他物体,开淡黄色小花。果实为椭圆形或圆形,多汁,味酸甜,可食用或酿酒。 [{"value": "[葡萄]落叶藤本植物。叶子呈掌状分裂,圆锥花序,茎有卷须,可缠绕其他物体,开淡黄色小花。果实为椭圆形或圆形,多汁,味酸甜,可食用或酿酒。", "example": []}]

总结

本文主要介绍了利用Python处理文本数据,并穿插了一些Pandas小技巧

共勉~

你可能感兴趣的:(数据分析,python,数据分析)