美年AI大赛--开源代码学习笔记(二)

5.简单特征数据处理

将特征数据进行简单处理,分割为数值型与字符型两个文件

5.1代码

def simple_nums_feature():
    '''
    将原始特征分割为数值型特征和字符型特征
    生成两个文件
    :return:
    '''
    path = './data/'
    data_pattern = path + 'feature_nums_df.csv'
    if os.path.exists(data_pattern):
        return
    filename = 'raw_feature.csv'  # 之前生成的原始特征

    train_df = pd.read_csv(path + filename, encoding='gbk', low_memory=False)
    print('数据载入成功')

    feature_num = train_df.shape[1]
    print('特征数量:', feature_num)

    # 将值为'未查''弃查'的全部改为nan
    train_df.replace(['未查', '弃查'], np.nan, inplace=True)
    print('丢弃了弃查未查项')

    # 丢弃空缺值超过57109项的【特征】
    missing_df = train_df.notnull().sum(axis=0).reset_index()  # 求每一列(每个检查项/特征)的非空个数
    missing_df.columns = ['column_name', 'count']  # 列名,非空个数
    missing_df = missing_df.sort_values(by='count', ascending=True)  # 按照count字段的值升序排序
    # 留下样本数在200以上的特征
    missing_df = missing_df[missing_df['count'] > 200]  # 按照布尔数组索引,留下true    # missing_df = missing_df[missing_df['count'] < 57298]

    # vid和列名组成新的,此时missing_df的原count列已经没用了,因为已经计数筛选完毕了
    # missing_df = ['vid'] + list(missing_df['column_name'])
    # 为什么要加[:-1]  由于之前升序排序的原因,vid被排到了最后,需要将其过滤掉
    missing_df = ['vid'] + list(missing_df['column_name'][:-1])

    # print(missing_df) #['vid', '2406', '0501',...,'vid']
    # 按照missing_df(里面是列名)重新对列进行索引,也就是把特征样本太少(特征对应样本数<200)的特征(列)给过滤掉了
    # 且对列进行了排序,排在前面的是样本少的特征
    train_df = train_df[missing_df]
    feature_num = train_df.shape[1]
    print('过滤后特征数量:', feature_num)

    ## 数一数每一项的unique值有多少,丢弃只有一项的
    # cat_dit = {}
    # for i in train_df.columns[1:]:  # 遍历每个特征(列),除去vid    #     c = train_df[i]  # 取出该列所有的值
    #     c = c[c.notnull()]  # 按照布尔数组过滤,留下为true的,丢弃false    #     c = c.unique().shape[0] #报错?
    #     cat_dit.update({i: c})
    # cat_dit = pd.Series(cat_dit).sort_values(ascending=False)
    ## cat_dit[cat_dit > 1].index   将过滤后的dit索引取出来
    # lst_str = ['vid'] + list(cat_dit[cat_dit > 1].index)
    # train_df = train_df[lst_str]
    # print(len(lst_str))

    # 开始转化
    count = 0
    lst = ['vid']
    lst_str = ['vid']
    for i in train_df.columns[1:]:
        if count % 50 == 0:
            print(count, '/', feature_num)  # 打印一下
        try:
            train_df[i] = train_df[i].astype('float')  # 将其对应的值(列向量)取出来,直接转化雷星
            lst.append(i)  # 如果是纯数值的直接append
        except:
            lst_str.append(i)  # 如果是非数值的,将其appedlst_str
        count += 1
    print("数值型特征数:", len(lst))
    print("字符型特征数", len(lst_str))
    # 分别索引
    nums_df = train_df[lst]
    str_df = train_df[lst_str]
    # 生成文件
    nums_df.to_csv(path + 'feature_nums_df.csv', index=False)
    str_df.to_csv(path + 'feature_str_df.csv', index=False)

5.2 运行结果



6.清洗混入少量字符的数值特征

 

6.1 代码

def more_nums_feature():
    '''
    处理含有少量字符特征的文件
    :return:
    '''
    path = './data/'
    data_pattern = path + 'feature_nums_waiteforprocess_df.csv'
    if os.path.exists(data_pattern):
        return

    filename = 'feature_str_df.csv'  # 之前生成的字符特征文件
    train_df = pd.read_csv(path + filename, encoding='gbk', low_memory=False)
    print('数据载入成功', '特征数量', train_df.shape[1])
    times = 0
    dit = {}
    for i in train_df.columns[1:]:  # 遍历每一列  去掉vid
        times += 1
        if times % 50 == 0:
            print("正在处理第", times, "个特征")  # 打印次数
        count = 0
        notnull_num = train_df[i].notnull().sum()  # 计算非空特征数
        for j in range(train_df.shape[0]):  # 遍历此列的每一个元素
            try:
                c = float(train_df[i][j])  # 试图转化为float
                if c is np.nan:
                    pass  # do nothing
                else:
                    count += 1  # 转化完成记个数
            except:
                pass  # do nothing
        # 使用字典统计转化成功的数值型在非空样本中的比例
        dit.update({i: count / notnull_num})

    # 将字典转化为Series类型并降序排序
    dit = pd.Series(dit).sort_values(ascending=False)
    print(dit.describe())  # 查看描述
    # dit[dit <= 0.05].index    将过滤后的dit的索引取出来
    num_df = train_df[['vid'] + list(dit[dit >= 0.5].index)]  # 数字型样本集
    str_df = train_df[['vid'] + list(dit[dit <= 0.05].index)]  # 字符型样本集
    temp = dit[dit < 0.5]
    temp = temp[temp > 0.05]  # >0.05<0,.5的中间部分
    other_df = train_df[['vid'] + list(temp.index)]  # 其他型样本集

    print("进一步确定数值型特征数:", num_df.shape[1] - 1)
    print("进一步确定字符型特征数:", str_df.shape[1] - 1)
    print("进一步确定其他型特征数:", other_df.shape[1] - 1)

    num_df.to_csv('./data/feature_nums_waiteforprocess_df.csv', index=False)
    str_df.to_csv('./data/feature_str2_df.csv', index=False)
    other_df.to_csv('./data/feature_unknown_df.csv', index=False)
    print("进一步分割文件完毕")

结果




6.清洗混入少量字符的数值特征(2)

6.1 代码

def more_nums_feature2():
    '''
    继续清洗含少量字符特征的数据
    :return:
    '''
    path = './data/'
    data_pattern = path + 'feature_nums2_df.csv'
    if os.path.exists(data_pattern):
        return

    filename = 'feature_nums_waiteforprocess_df.csv'  # 之前生成的数值型特征样本

    df = pd.read_csv(path + filename, encoding='gbk', low_memory=False)
    print('数据载入成功', '特征数量', df.shape[1])

    '''
    re.compile
    一个正则表达式要重复使用几千次,
    出于效率的考虑,可以预编译该正则表达式,
    接下来重复使用时就不需要编译这个步骤了,直接匹配

*表示任意个字符(包括0个)
+表示至少一个字符
?表示0个或1个字符
{n}表示n个字符
{n,m}表示n-m个字符
+?表示非贪婪匹配
    
    .可以匹配任意字符(数字字母标点)
    
    ^  匹配字符串的开头
    $  匹配字符串的末尾
    [^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
    
    '''
    pat = [re.compile(r'\d+\d+'), re.compile(r'[^\d.]'), re.compile(r'\.$')]

    cout = 0
    t0 = time.time()  # 初始化时间
    for i in df.columns[1:]:  # 遍历每一列
        cout += 1  # 计数
        lst = []
        if cout % 10 == 0:
            print('10个用时{}'.format(time.time() - t0))  # 计时
            print(cout, '/', df.shape[1])  # 处理进度 处理列数/总列数
            t0 = time.time()  # 更新时间
        for j in range(df[i].shape[0]):  # 遍历此列的每个元素
            c = str(df[i][j])  # 读取df并转化为字符串
            if c == 'nan' or (not c) or (c is np.nan):  # 如果是nan直接跳过
                continue
            for k in range(len(pat)):
                if c:
                    # 将时间字符串清除掉
                    c = re_sub(pat[k], c)
                else:
                    break
            df[i][j] = c
            try:
                c = float(df[i][j])
            except:
                pass

    for i in df.columns[1:]:  # 遍历所有列
        # 转化为数值类型,默认情况下,它不能处理字母型的字符串
        df[i] = pd.to_numeric(df[i], errors='coerce')

    df.to_csv('./data/feature_nums2_df.csv', index=False)
 
def re_sub(pat, s):
    '''
    re.sub用于替换字符串中的匹配项
    re.sub(pattern, repl, string, count=0, flags=0)
    pattern : 正则中的模式字符串。
    repl : 替换的字符串,也可为一个函数。
    string : 要被查找替换的原始字符串。
    count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

    '''
    # 将时间日期替换成空格,其他不变,将组合好的字符串返回
    c = re.sub(pat, ' ', s).strip().split()
    if c:
        return c[0]
    else:
        return c


输出

6.2 知识点--python正则表达式

两篇不错的教程:

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193331387014ccd1040c814dee8b2164bb4f064cff000

http://www.runoob.com/python/python-reg-expressions.html


6.2.1 正则表达式规则


    用*表示任意个字符(包括0个)
    用+表示至少一个字符
    用?表示0个或1个字符
    用{n}表示n个字符
    用{n,m}表示n-m个字符
    用+?表示非贪婪匹配
    
    .可以匹配任意字符(数字字母标点)
    
    ^ 匹配字符串的开头
    $ 匹配字符串的末尾
    [^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。

6.2.2 re类库

(1)编译 compile

    一个正则表达式要重复使用几千次,
    出于效率的考虑,可以预编译该正则表达式,
    接下来重复使用时就不需要编译这个步骤了,直接匹配


(2)替换 sub

    re.sub用于替换字符串中的匹配项

    re.sub(pattern, repl, string, count=0, flags=0)
    pattern : 正则中的模式字符串。
    repl : 替换的字符串,也可为一个函数。
    string : 要被查找替换的原始字符串。

    count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配


(3)例子

a = re.compile(r'\d+月\d+日')
aa = re.match(a, '12月10日')
if aa:
    print('ok')
    
c = re.sub(r'\d+月\d+日', '0', '12月5日33').strip().split()
if c:
    print(c[0])
else:

    print(c)


输出:




7.清洗字符特征

7.1 代码

def wash_str():
    '''
    清洗字符特征
    :return:
    '''
    path = './data/'
    data_pattern = path + 'feature_str3_df.csv'
    if os.path.exists(data_pattern):
        return
    filename = 'feature_str2_df.csv'

    train_df = pd.read_csv(path + filename, encoding='gbk', low_memory=False)  # 读取文件
    print('数据载入成功', '特征数量', train_df.shape[1])

    # 正则化 此字符可以字母,数字与标点符号
    # r'' 是因为python字符串本身也需要转义,使用Pythonr前缀,就不用考虑转义的问题了,可以使对应的正则表达式字符串不变:
    p = re.compile(r'[a-zA-Z0-9<>/,.,。()():/*% ""“”‘’°?=~;;::]')
    word_lst = set(['未见异常', '未见明显异常', '正常', '未见异常未见异常', '正常正常', '正常正常正常', '未闻及异常',
                    '正常正常正常正常正常', '未发现异常', '未见异常未见异常未见异常', '未见明显异常未见异常', '未发现明显异常',
                    '未发现异常未发现异常', '正常正常正常正常', '', '未见', '未见未见', '阴性', '阴性-', '阴性阴性', '-阴性',
                    '阴性阴性-', '阴性-阴性-', '抗体阴性', '-', '-~', '--'])
    count = 0
    t0 = time.time()
    for i in train_df.columns[1:]:  # 遍历每一列
        count += 1
        if count % 10 == 0:
            print('10个用时{}'.format(time.time() - t0))
            t0 = time.time()
            print(count, '/', train_df.shape[1])
        for j in range(train_df[i].shape[0]):
            # 如果是nan直接跳过
            if str(train_df[i][j]) == 'nan' or (not train_df[i][j]) or (train_df[i][j] is np.nan):
                continue

            # 将字母、数字和标点删除
            train_df[i][j] = re.sub(p, '', train_df[i][j]).strip()
            # 如果在此词表中,全部替换为正常
            if train_df[i][j] in word_lst:  #
                train_df[i][j] = '正常'  # 过滤字符串

    train_df.to_csv('./data/feature_str3_df.csv', index=False)


8 切分为长短字符串

8.1 代码

def cut_str_feature():
    '''
    切分为长字符串和短字符串
    '''
    path = './data/'
    data_pattern = path + 'feature_long_str.csv'
    if os.path.exists(data_pattern):
        return
    filename = 'feature_str3_df.csv'  # 之前清洗完的(删除了标点,啰嗦正常表意被替换为正常)

    train_df = pd.read_csv(path + filename, encoding='gbk', low_memory=False)  # 读取文件
    nums, nums2 = train_df.shape
    count = 0
    max_length = 0
    lst = []

    '''
    collections.Counter() 
    hashable对象计数,是字典的子类
    Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,
    其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)
    '''
    dit = collections.Counter()
    len_dit = {}
    for i in train_df.columns[1:]:  # 遍历每一列
        max_i = 0
        if count % 10 == 0:
            print(count, '/', nums2)  # 已处理的列数/总列数
        for j in range(train_df[i].shape[0]):  # 遍历此列的每个元素
            '''
            jieba:分词模块
            '''
            c = list(jieba.cut(str(train_df[i][j]).encode('utf-8')))
            lst.append(c)
            max_i = max(len(c), max_i)  # 找到分词组数最长的那个
            # 更新一条 i: max_i 记录
            len_dit.update({i: max_i})
            # 增加方法 使用另一个iterable对象更新
            dit.update(c)  # 计数
        count += 1
    df = pd.Series(dit).sort_values(ascending=False)  # 对词频统计进行降序排序
    len_df = pd.Series(len_dit).sort_values(ascending=False)  # 按照分词数最大值对每一列索引进行降序排序
    print('cut over')
    # print('max_length', max_length)
    print(len_df.describe())
    print(len_df.head(5))
    #6是一个超参数 如果每列的最长分词数<6则认为其为短字符串列
    feature_lst = ['vid'] + list(len_df[len_df < 6].index)  # 将过滤后的样本索引取出来,与vid组成新的索引,一会要用
    # print(feature_lst)
    # ['vid', '1305', '0201', '0975', '8101', '0210', '1301', '0706', '0435', '1329', '0426', '0979', '3301', '0440', '3601', '0202', '0715', '0537', '0541', '0227', '0949', '0947', '0984', '3869', '0546', '0972', '729005', '0901', '3430', '3486', '100010', '0436', '3485', '0430', '3192', '2231', '21A236', '3190', '3195', '269041', '3197', '0403', '0406', '2233', '2229', '0413', '21A235', '0421', '1328', '0219', '0428', '659023', '659019', '769005', '0221', '0220', '0415', '659017', '0218', '0432', '0216', '0213', '0212', '0206', '0427', '0420', '659022', '0405', '0433', '659018', '0423', '0431', '0407', '0207', '659021', '659020', '0414', '0438', '0437', '0429', '659006', '659016', '0728', '3196', '3194', '3191', '659007', '300086', '30007', '300062', '300019', '300018', '30001', '269047', '269045', '269042', '269040', '659008', '3198', '3203', '3207', '3740', '659004', '659003', '659002', '659001', '3862', '3855', '3738', '3303', '3721', '3433', '3432', '3426', '3400', '3399', '2302', '2278', '2235', '659005', '0986', '0985', '0983', '0982', '0981', '0980', '0977', '100011', '0976', '0974', '0973', '659015', '0733', '0732', '100009', '659014', '2230', '1334', '2228', '21A238', '21A237', '659009', '659010', '1336', '1333', '659013', '1332', '659011', '1315', '1313', '659012', '1304', 'I49012']
    short_str_df = train_df[feature_lst]  # 按照新的索引取出内容
    values_dit = []
    unique_nums = []
    strange = []
    for i in short_str_df.columns[1:]:  # 遍历新的每一列
        temp = short_str_df[i]  # 取值
        u_nums = (temp.value_counts() > 1).sum()
        unique_nums.append(u_nums)
        if u_nums <= 10:
            values_dit += list(temp.unique())
        else:
            strange.append(i)

    unique_nums = pd.Series(unique_nums)
    unique_nums.describe()  # 统计

    short_str = list(set(short_str_df.columns[1:]) - set(['0213']))  # 减去了一列 项目描述里面有
    long_str = list(set(train_df.columns[1:]) - set(short_str))#减去短索引,剩下的索引就是长索引
    np.save(path + 'category_feature_map.npy', short_str)
    short_str = ['vid'] + short_str
    long_str = ['vid'] + long_str
    short_str_df = train_df[short_str]
    long_str_df = train_df[long_str]

    short_str_df.to_csv(path + 'feature_short_str.csv', index=False)
    long_str_df.to_csv(path + 'feature_long_str.csv', index=False)


输出


8.2 知识点---统计 collections.Counter() 

    collections.Counter() 
    为hashcollections.Counter() able对象计数,是字典的子类
    Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,
    其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)

8.3 知识点---分词 jieba


两篇入门博客:

https://www.cnblogs.com/jiayongji/p/7119065.html

https://blog.csdn.net/reims2046/article/details/72869337


8.4 例子

dit = collections.Counter()
sss='我也不知道我是谁'
c=list(jieba.cut(str(sss).encode('utf-8')))
print(c)
print('---------------')
dit.update(c)
print(dit)
print('---------------')
df = pd.Series(dit).sort_values(ascending=False)

print(df)

输出:









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值