zoukankan      html  css  js  c++  java
  • 数据结构与算法 (03)

    继续来进行 pythonCookbook. 学习大佬代码使我快乐, 与其自己写, 还不如直接抄, 然后加上自己的注解, 这样才是最适合我的学习之道哇.

    仰之弥高,钻之弥坚

    老祖宗还是厉害呀, 要是我能认识到这种境界, 夫复何求呦.

    序列元素去重并保持顺序

    需求

    在一个序列上, 保持元素顺序的前提下, 对相同元素值进行去重

    方案

    如果序列中的值都是 hashable 类型的, 那直接用 集合 set 或者 生成器 就轻松解决了.

    def filter_dumplicate(items):
        """序列元素去重"""
        seen = set()
        for item in items:
            if item not in seen:
                # 所有的 yield item 组成 生成器对象
                yield item 
                seen.add(item)
    
    # test            
    a = [1, 5, 2, 1, 9, 1, 5, 10]
    print(list(filter_dumplicate(a)))
    
    [1, 5, 2, 9, 10]
    

    我感觉, 这个大佬, 是不是搞得太复杂, 还是我 too simple ?

    # 这样不更简单吗
    ret = []
    for i in a:
        if i not in ret:
            ret.append(i)
    print(ret)
    
    [1, 5, 2, 9, 10]
    

    就去个重, 私以为不用那么麻烦的吧. 其实更多的是, 我们通常不用考虑其顺序, 就去重, 这样一来, 直接用集合, 一波带走.

    print(set(a))  # 集合元素的顺序, 呃,,我也不清楚
    
    {1, 2, 5, 9, 10}
    

    这类写法, 针对元素是 hashable 的时候ok的, 但对于不可哈希, 如 dict 类型 的序列, 要去重元素的话, 也可以这样.

    def dedupe(items, key=None):
        """字典元素去重"""
        seen = set()
        for item in items:
            # key 有值则为dict,取其值, else 是单序列值
            val = item if key is None else key(item)
            if val not in seen:
                yield item 
                seen.add(val)
        
    # test 以后写函数, 优先用 yield 而不用 list 来不断 append 啦
    a = [
        {'x':1, 'y':2}, {'x':1, 'y':3},
        {'x':1, 'y':2}, {'x':2, 'y':4}
        ]
    
    
    print(list(dedupe(a, key=lambda d: (d['x'], d['y']))))
    
    print(list(dedupe(a, key=lambda d: d['x'])))
    
    [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
    [{'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
    

    这个把函数改为生成器, 确实优雅呀, 学到了, 以后, 优先考虑哦. 不要再傻傻地只会 append 啦. 基于此中方法, 再对某单个字段或者属性, 或者更复杂的 数据结构来去重, 也是ok的.

    可以发现, 针对于序列的遍历, 生成器 yield 则会更加通用, 而非以前傻傻滴, 先定义个空 lst , 过程中不断 append 这样就造成浪费内存了, yield 返回就行啦, 最后再统一 处理即可. 生成器, 对于文件中, 消除重复行, 也是可以的, 套路都固定的.

    with open(some_file, 'r') as f:
        for line in dedupe(f):
            ....
    

    命名切片

    需求

    清理一大堆, 已经无法直视的硬编码切片下标.

    方案

    假设我们有一个切片, 是一个字符串中固定的位置, 如字符串或文件等.

    ###### 0123456789012345678901234567890123456789012345678901234567890' record = '....................100 .......513.25 ..........' cost = int(record[20:23]) * float(record[31:37])
    record = '....................100 .......513.25 ..........' cost = int(record[20:23]) * float(record[31:37])
    cost = int(record[20:23]) * float(record[31:37])
    

    如果这样写切片, 维护起来简直崩溃, 于是呢,应该而给切片进行命名呀.

    shares = slice(20, 33)
    

    即用内置的 slice() 函数 创建一个切片对象, 可以被用在, 任何允许使用的方法.

    items = [0, 1, 2, 3, 4, 5, 6]
    
    # 前闭后开
    a = slice(2,4)
    
    print(items[2:4])
    print(items[a])
    
    items[a] = [88,99]
    print(items)
    
    del items[a]
    print(items)
    
    
    [2, 3]
    [2, 3]
    [0, 1, 88, 99, 4, 5, 6]
    [0, 1, 4, 5, 6]
    

    对于一个切片对象a, 还可以调用其 a.start, a.stop 等属性获取更多信息.

    print(a)
    print(a.start)
    print(a.stop)
    
    slice(2, 4, None)
    2
    4
    个人感觉这玩意儿, 也没什么用呀.
    

    序列中出现最多的元素

    需求

    找出序列中, 出现最多次数的元素.

    方案

    我做数据工作, 就会经常做这类的事情, 但我从来没有用过内置工具, 都是, 以字典的方式来进行词频统计, 感觉也行.

    内置的 collections.Counter 类, 就专门来解决此类问题的, most_common() 方法, 很厉害的哦.

    词频统计, 最为经典的就是.

    words = [ 'look', 'into', 'my', 'eyes', 'look', 'into', 
             'my', 'eyes', 'the', 'eyes', 'the', 'eyes', 'the', 
             'eyes', 'not', 'around', 'the', 'eyes', "don't", 
             'look', 'around', 'the', 'eyes', 'look', 'into', 
             'my', 'eyes', "you're", 'under' 
            ] 
    
    from collections import Counter 
    
    # 初始化Counter 对象, 将统计序列加进去
    word_counts = Counter(words)
    print(word_counts.most_common()) 
    # 选出频率最高的3个词
    print("--"*8)
    top_three = word_counts.most_common(3)
    print(top_three)
    
    [('eyes', 8), ('the', 5), ('look', 4), ('into', 3), ('my', 3), ('around', 2), ('not', 1), ("don't", 1), ("you're", 1), ('under', 1)]
    ----------------
    [('eyes', 8), ('the', 5), ('look', 4)]
    

    作为输入, Counter 对象可以接受任意, 可哈希元素构成的序列对象. 在底层的实现上, 一个 Counter 对象就是一个车字典, 将元素映射到它出现的次数上.

    # Counter 对象, 其实就是字典, value 是频次
    print(word_counts['eyes'])
    print(word_counts['not'])
    
    8
    1
    

    结论就是, 词频统计之类的, 可以优先考虑 Counter 对象, 当然, 我手动弄字典也可以呀, 我乐意, 又不难.

    过滤序列元素

    需求

    过滤序列元素, 根据某些规则

    方案

    最为简单和我最喜欢用的是, 列表推导式.

    my_list = [1, 4, -5, 10, 2, 3, -1]
    
    # 过滤出 大于 0 的元素
    print([i for i in my_list if i > 0])
    
    # 小于0, 且是 "奇数"
    print([i for i in my_list if i < 0 and i % 2 == 1])
    
    [1, 4, 10, 2, 3]
    [-5, -1]
    

    用列表推导式的一个问题在于, 当输入很大, 则会得到一个大的结果集, 浪费内存, 解决方案就是用 迭代器呀.

    #  生成器就节约内存了, 优先考虑
    pos = (i for i in my_list if i > 0 and i % 2 == 0)
    print(pos)
    
    for i in pos:
        print(i)
    
    <generator object <genexpr> at 0x00000217B924F938>
    4
    10
    2
    

    有时候, 过滤条件复杂, 就需要把过滤的规则给放到一个函数中啦, 然后将其放到内置的 filter() 函数中.

    values = ['1', '2', '-3', '-', '4', 'N/A', '5']
    
    def is_int(val):
        try:
            x = int(val)
            return True
        except ValueError:
            return False
        
    # test
    list(filter(is_int, values))
    
    ['1', '2', '-3', '4', '5']
    

    我感觉这 filter 和 map 函数的用法是差不多的, 一个是过滤出, True 的, 一个是做映射, 映射到每个元素. 同时, filter() 函数创建了一个迭代器.

    想想, 还是列表推导式, 更加爽一点哦, 比如, 在推导的时候, 还是可以做数据转换的.

    lst = [1, 4, -5, 10, -9, 2, 3, -1]
    
    import math
    print([math.sqrt(i) for i in lst if i > 0])
    
    [1.0, 2.0, 3.1622776601683795, 1.4142135623730951, 1.7320508075688772]
    

    不仅仅可以过滤, 结合 if - else 还能实现赋值.

    # RELU 函数
    print([i if i > 0 else 0 for i in my_list])
    
    [1, 4, 0, 10, 2, 3, 0]
    

    小结

    • 序列去重且有序, 用集合 set 和 生成器 yield, 优先考虑
    • 切片对象, 可用 slice 函数来创建命名对象, 便于管理
    • 序列统计, 如词频统计, 优先用 collectiions.Counter().most_common() 方法, 自己写也不难.
    • 过滤序列, 用列表推导式和生成器(元组推导式), 复杂结合 filter 函数, 结合 [a if aaa else b for aaa in xxx ]
  • 相关阅读:
    sql去重复
    验证 decimal 和 数字
    OleDbConnection读取Excel
    排班知识点
    sql基础
    SQL Server 获取月份的具体天数
    2016 Excel 展示 Sqlserver数据并制作图表
    SQL Server跨域查询
    SqlSugar中CASE WHEN的用法
    Microsoft.AspNetCore.Mvc.Versioning学习笔记
  • 原文地址:https://www.cnblogs.com/chenjieyouge/p/12879740.html
Copyright © 2011-2022 走看看