精华内容
下载资源
问答
  • pandas系列学习(七):数据透视

    千次阅读 多人点赞 2018-10-31 14:58:50
    作者:chen_h 微信号 & QQ:862251340 微信公众号:coderpai ...pandas系列学习(一):pandas入门 ...pandas系列学习(二):Series ...pandas系列学习(七):数据透视 介绍 大多...

    作者:chen_h
    微信号 & QQ:862251340
    微信公众号:coderpai


    pandas系列学习(一):pandas入门

    pandas系列学习(二):Series

    pandas系列学习(三):DataFrame

    pandas系列学习(四):数据提取

    pandas系列学习(五):数据连接

    pandas系列学习(六):数据聚合

    pandas系列学习(七):数据透视表


    介绍

    大多数人可能都有使用 Excel 中的数据透视表的经验。 pandas 提供了一个类似的功能,称为 pivot_table。虽然它非常有用,但我经常发现自己很难记住如何使用语法格式化输出以满足我的需求。本文将重点介绍 pandas pivot_table 函数以及如何将其用于数据分析。

    作为一个额外的奖励,我创建了一个简单的备忘录,总结了 pivot_table 。你可以在这篇文章的最后找到它,我希望它是一个有用的参考。

    数据

    使用 pandas 的 pivot_table 的一个挑战是确保你了解你的数据以及你尝试使用数据透视表来处理问题。这是一个看似简单的功能,但可以非常快速的产生非常强大的分析。

    在这种情况下,我将跟踪销售渠道(也称为渠道)。基本问题是一些销售周期很长,管理层希望全年更详细的了解它。

    典型问题包括:

    • 管道中有多少收入?
    • 什么产品正在筹备中?
    • 谁在什么阶段有什么产品?
    • 我们有多大可能在年底前完成交易?

    许多公司将拥有销售用于跟踪流程的 CRM 工具或其他软件。虽然他们可能有分析数据的有用工具,但不可避免的有人会将数据导出到 Excel 并使用数据透视表来汇总数据。

    使用 padnas 的数据透视表可能是一个很好的选择,因为它是:

    • 更快(一旦设置)
    • 自我记录(查看代码,你知道它做了什么)
    • 易于使用生成报告或者电子邮件
    • 更灵活,因为你可以定义客户关心的内容

    读入数据

    让我们先建立我们的环境,数据你可以点击这个下载。然后将我们的销售数据读入 DataFrame ,如下:

    import pandas as pd
    import numpy as np
    
    df = pd.read_excel("./sales-funnel.xlsx")
    df.head()
    
    Account Name Rep Manager Product Quantity Price Status
    0 714466 Trantow-Barrows Craig Booker Debra Henley CPU 1 30000 presented
    1 714466 Trantow-Barrows Craig Booker Debra Henley Software 1 10000 presented
    2 714466 Trantow-Barrows Craig Booker Debra Henley Maintenance 2 5000 pending
    3 737550 Fritsch, Russel and Anderson Craig Booker Debra Henley CPU 1 35000 declined
    4 146832 Kiehn-Spinka Daniel Hilton Debra Henley CPU 2 65000 won

    为了方便起见,我们将状态列定义为类别并设置我们要查看的顺序。

    这不是严格要求的,但可以帮助我们在分析数据时保持我们想要的顺序。

    df["Status"] = df["Status"].astype("category")
    df["Status"].cat.set_categories(["won","pending","presented","declined"],inplace=True)
    

    透视数据

    在我们构建数据透视表时,我认为最简单的方法就是一步一步。添加项目并检查每个步骤以验证你是否获得了预期的结果。不要害怕使用订单和变量来查看哪些需求。

    最简单的数据透视表必须具有数据框和索引。在这种情况下,让我们使用 Name 作为索引。

    pd.pivot_table(df,index=["Name"])
    
    Account Price Quantity
    Name
    Barton LLC 740150.0 35000.0 1.000000
    Fritsch, Russel and Anderson 737550.0 35000.0 1.000000
    Herman LLC 141962.0 65000.0 2.000000
    Jerde-Hilpert 412290.0 5000.0 2.000000
    Kassulke, Ondricka and Metz 307599.0 7000.0 3.000000
    Keeling LLC 688981.0 100000.0 5.000000
    Kiehn-Spinka 146832.0 65000.0 2.000000
    Koepp Ltd 729833.0 35000.0 2.000000
    Kulas Inc 218895.0 25000.0 1.500000
    Purdy-Kunde 163416.0 30000.0 1.000000
    Stokes LLC 239344.0 7500.0 1.000000
    Trantow-Barrows 714466.0 15000.0 1.333333

    你也可以拥有多个索引。是加上,大多数的 pivot_table args 都可以通过列表获取多个值。

    pd.pivot_table(df,index=["Name","Rep","Manager"])
    
    Account Price Quantity
    Name Rep Manager
    Barton LLC John Smith Debra Henley 740150.0 35000.0 1.000000
    Fritsch, Russel and Anderson Craig Booker Debra Henley 737550.0 35000.0 1.000000
    Herman LLC Cedric Moss Fred Anderson 141962.0 65000.0 2.000000
    Jerde-Hilpert John Smith Debra Henley 412290.0 5000.0 2.000000
    Kassulke, Ondricka and Metz Wendy Yule Fred Anderson 307599.0 7000.0 3.000000
    Keeling LLC Wendy Yule Fred Anderson 688981.0 100000.0 5.000000
    Kiehn-Spinka Daniel Hilton Debra Henley 146832.0 65000.0 2.000000
    Koepp Ltd Wendy Yule Fred Anderson 729833.0 35000.0 2.000000
    Kulas Inc Daniel Hilton Debra Henley 218895.0 25000.0 1.500000
    Purdy-Kunde Cedric Moss Fred Anderson 163416.0 30000.0 1.000000
    Stokes LLC Cedric Moss Fred Anderson 239344.0 7500.0 1.000000
    Trantow-Barrows Craig Booker Debra Henley 714466.0 15000.0 1.333333

    这很有趣但不是特别有用。我们可能想要做的是通过 Manager 和 Rep 查看。通过更改索引可以轻松完成。

    pd.pivot_table(df,index=["Manager","Rep"])
    
    Account Price Quantity
    Manager Rep
    Debra Henley Craig Booker 720237.0 20000.000000 1.250000
    Daniel Hilton 194874.0 38333.333333 1.666667
    John Smith 576220.0 20000.000000 1.500000
    Fred Anderson Cedric Moss 196016.5 27500.000000 1.250000
    Wendy Yule 614061.5 44250.000000 3.000000

    你可以看到数据透视表非常智能,可以通过将 reps 与 manager 分组来开始汇总数据并对其进行汇总。现在我们开始了解数据透视表可以为我们做些什么。

    为此,“账户” 和 “数量” 列不真正有用。让我们通过使用 values 字段显式定义我们关心的列来删除它。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price"])
    
    Price
    Manager Rep
    Debra Henley Craig Booker 20000.000000
    Daniel Hilton 38333.333333
    John Smith 20000.000000
    Fred Anderson Cedric Moss 27500.000000
    Wendy Yule 44250.000000

    价格列自动平均数据,但我们可以进行计数或者总和。使用 aggfunc 和 np.sum 添加它们很简单。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price"],aggfunc=np.sum)
    
    Price
    Manager Rep
    Debra Henley Craig Booker 80000
    Daniel Hilton 115000
    John Smith 40000
    Fred Anderson Cedric Moss 110000
    Wendy Yule 177000

    aggfunc 可以获取一系列函数。让我们尝试使用 np.mean 函数和 len 来计算。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price"],aggfunc=[np.mean,len])
    
    mean len
    Price Price
    Manager Rep
    Debra Henley Craig Booker 20000.000000 4
    Daniel Hilton 38333.333333 3
    John Smith 20000.000000 2
    Fred Anderson Cedric Moss 27500.000000 4
    Wendy Yule 44250.000000 4

    如果我们想要查看按产品细分的销售额,则 columns 变量允许我们定义一个或者多格列。

    我认为 pivot_table 的一个令人困惑的问题是使用列和值。请记住,列是可选的 —— 它们提供了一种额外的方法来细分你关心的实际值。聚合函数将应用于你列出的值。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price"],
                   columns=["Product"],aggfunc=[np.sum])
    
    sum
    Price
    Product CPU Maintenance Monitor Software
    Manager Rep
    Debra Henley Craig Booker 65000.0 5000.0 NaN 10000.0
    Daniel Hilton 105000.0 NaN NaN 10000.0
    John Smith 35000.0 5000.0 NaN NaN
    Fred Anderson Cedric Moss 95000.0 5000.0 NaN 10000.0
    Wendy Yule 165000.0 7000.0 5000.0 NaN

    NaN 有点让人抓狂。如果我们想要删除它们,我们可以使用 fill_value 将它们设置为 0 。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price"],
                   columns=["Product"],aggfunc=[np.sum],fill_value=0)
    
    sum
    Price
    Product CPU Maintenance Monitor Software
    Manager Rep
    Debra Henley Craig Booker 65000 5000 0 10000
    Daniel Hilton 105000 0 0 10000
    John Smith 35000 5000 0 0
    Fred Anderson Cedric Moss 95000 5000 0 10000
    Wendy Yule 165000 7000 5000 0

    我认为添加数量列也是非常有用的,将数量添加到值列表中。

    pd.pivot_table(df,index=["Manager","Rep"],values=["Price","Quantity"],
                   columns=["Product"],aggfunc=[np.sum],fill_value=0)
    
    sum
    Price Quantity
    Product CPU Maintenance Monitor Software CPU Maintenance Monitor Software
    Manager Rep
    Debra Henley Craig Booker 65000 5000 0 10000 2 2 0 1
    Daniel Hilton 105000 0 0 10000 4 0 0 1
    John Smith 35000 5000 0 0 1 2 0 0
    Fred Anderson Cedric Moss 95000 5000 0 10000 3 1 0 1
    Wendy Yule 165000 7000 5000 0 7 3 2 0

    有趣的是,你可以将项目移动到索引以获得不同的可视化结果。从列中删除产品并添加都索引中。

    pd.pivot_table(df,index=["Manager","Rep","Product"],
                   values=["Price","Quantity"],aggfunc=[np.sum],fill_value=0)
    
    sum
    Price Quantity
    Manager Rep Product
    Debra Henley Craig Booker CPU 65000 2
    Maintenance 5000 2
    Software 10000 1
    Daniel Hilton CPU 105000 4
    Software 10000 1
    John Smith CPU 35000 1
    Maintenance 5000 2
    Fred Anderson Cedric Moss CPU 95000 3
    Maintenance 5000 1
    Software 10000 1
    Wendy Yule CPU 165000 7
    Maintenance 7000 3
    Monitor 5000 2

    对于此数据集,此表示更有意义。现在,如果我想看一些总数怎么办?marginins = True 可以帮助我们实现。

    pd.pivot_table(df,index=["Manager","Rep","Product"],
                   values=["Price","Quantity"],
                   aggfunc=[np.sum,np.mean],fill_value=0,margins=True)
    
    sum mean
    Price Quantity Price Quantity
    Manager Rep Product
    Debra Henley Craig Booker CPU 65000 2 32500 1.000000
    Maintenance 5000 2 5000 2.000000
    Software 10000 1 10000 1.000000
    Daniel Hilton CPU 105000 4 52500 2.000000
    Software 10000 1 10000 1.000000
    John Smith CPU 35000 1 35000 1.000000
    Maintenance 5000 2 5000 2.000000
    Fred Anderson Cedric Moss CPU 95000 3 47500 1.500000
    Maintenance 5000 1 5000 1.000000
    Software 10000 1 10000 1.000000
    Wendy Yule CPU 165000 7 82500 3.500000
    Maintenance 7000 3 7000 3.000000
    Monitor 5000 2 5000 2.000000
    All 522000 30 30705 1.764706

    让我们将分析提升到一个水平,并在 manager 级别查看我们的数据管道。请注意如何根据我们之前的类别定义对状态进行排序。

    pd.pivot_table(df,index=["Manager","Status"],values=["Price"],
                   aggfunc=[np.sum],fill_value=0,margins=True)
    
    sum
    Price
    Manager Status
    Debra Henley won 65000
    pending 50000
    presented 50000
    declined 70000
    Fred Anderson won 172000
    pending 5000
    presented 45000
    declined 65000
    All 522000

    一个非常方便的功能是能够将字典传递给 aggfunc,因此你可以对你选择的每个值执行不同的功能。这具有使标签更清洁的作用。

    pd.pivot_table(df,index=["Manager","Status"],columns=["Product"],values=["Quantity","Price"],
                   aggfunc={"Quantity":len,"Price":np.sum},fill_value=0)
    
    Price Quantity
    Product CPU Maintenance Monitor Software CPU Maintenance Monitor Software
    Manager Status
    Debra Henley won 65000 0 0 0 1 0 0 0
    pending 40000 10000 0 0 1 2 0 0
    presented 30000 0 0 20000 1 0 0 2
    declined 70000 0 0 0 2 0 0 0
    Fred Anderson won 165000 7000 0 0 2 1 0 0
    pending 0 5000 0 0 0 1 0 0
    presented 30000 0 5000 10000 1 0 1 1
    declined 65000 0 0 0 1 0 0 0

    你也可以提供要应用于每个值的聚合函数列表:

    table = pd.pivot_table(df,index=["Manager","Status"],columns=["Product"],values=["Quantity","Price"],
                   aggfunc={"Quantity":len,"Price":[np.sum,np.mean]},fill_value=0)
    table
    
    Price Quantity
    mean sum len
    Product CPU Maintenance Monitor Software CPU Maintenance Monitor Software CPU Maintenance Monitor Software
    Manager Status
    Debra Henley won 65000 0 0 0 65000 0 0 0 1 0 0 0
    pending 40000 5000 0 0 40000 10000 0 0 1 2 0 0
    presented 30000 0 0 10000 30000 0 0 20000 1 0 0 2
    declined 35000 0 0 0 70000 0 0 0 2 0 0 0
    Fred Anderson won 82500 7000 0 0 165000 7000 0 0 2 1 0 0
    pending 0 5000 0 0 0 5000 0 0 0 1 0 0
    presented 30000 0 5000 10000 30000 0 5000 10000 1 0 1 1
    declined 65000 0 0 0 65000 0 0 0 1 0 0 0

    尝试将这一切全部拉到一起可能看起来非常疯狂,但是一旦你开始玩数据并慢慢添加项目,你就可以了解它是如何工作的。我的一般经验法则是,一旦你使用多个 group,你应该评估一个数据透视表是否是一个有用的方法。

    高级数据透视表过滤

    生成数据后,它就位于 DataFrame 中,因此你可以使用标准 DataFrame 函数对其进行过滤。

    如果你只想看一个 manager:

    table.query('Manager == ["Debra Henley"]')
    
    Price Quantity
    mean sum len
    Product CPU Maintenance Monitor Software CPU Maintenance Monitor Software CPU Maintenance Monitor Software
    Manager Status
    Debra Henley won 65000 0 0 0 65000 0 0 0 1 0 0 0
    pending 40000 5000 0 0 40000 10000 0 0 1 2 0 0
    presented 30000 0 0 10000 30000 0 0 20000 1 0 0 2
    declined 35000 0 0 0 70000 0 0 0 2 0 0 0

    我们可以查看所有待处理和赢得的交易。

    table.query('Status == ["pending","won"]')
    
    Price Quantity
    mean sum len
    Product CPU Maintenance Monitor Software CPU Maintenance Monitor Software CPU Maintenance Monitor Software
    Manager Status
    Debra Henley won 65000 0 0 0 65000 0 0 0 1 0 0 0
    pending 40000 5000 0 0 40000 10000 0 0 1 2 0 0
    Fred Anderson won 82500 7000 0 0 165000 7000 0 0 2 1 0 0
    pending 0 5000 0 0 0 5000 0 0 0 1 0 0

    这是 pivot_table 的强大功能,所以一旦你将数据转换为你需要的 pivot_table 格式,请不要忘记你拥有一个强大的功能。

    为了总结所有这些,我创建了一个被王丹,希望能帮助你记住 pandas pivot_table 的使用方法,如下:

    在这里插入图片描述

    展开全文
  • Influx Sql系列教程七:delete 删除数据

    千次阅读 2019-08-20 22:11:12
    前面介绍了使用insert实现新增和修改记录的使用姿势,接下来我们看一下另外一个简单的使用方式,如何删除数据 1. delete 语句 delete的官方语法如下 DELETE FROM <measurement_name> WHERE [<tag_key>=...

    前面介绍了使用insert实现新增和修改记录的使用姿势,接下来我们看一下另外一个简单的使用方式,如何删除数据

    1. delete 语句

    delete的官方语法如下

    DELETE FROM <measurement_name> WHERE [<tag_key>='<tag_value>'] | [<time interval>]
    

    delete语句和我们常见sql语法有点像,但是注意一下上面的where中的条件,只允许根据tag和时间来进行删除操作

    下面给出几个简单的例子

    case1 根据时间删除

    > select * from add_test
    name: add_test
    time                age boy  email            name  phone user_id
    ----                --- ---  -----            ----  ----- -------
    1564149327925320596 19  true bangzewu@126.com YiHui 110   0
    1564149920283253824 18  true bangzewu@126.com YiHui 110   21
    1564150279123000000 18  true bangzewu@126.com YiHui 110   22
    > delete from add_test where time>=1564150279123000000
    > select * from add_test
    name: add_test
    time                age boy  email            name  phone user_id
    ----                --- ---  -----            ----  ----- -------
    1564149327925320596 19  true bangzewu@126.com YiHui 110   0
    1564149920283253824 18  true bangzewu@126.com YiHui 110   21
    

    case2 根据tag删除

    注意name为保留名,因此需要用双引号括起来

    > show tag keys from add_test
    name: add_test
    tagKey
    ------
    name
    phone
    > delete from add_test where "name"='YiHui'
    > select * from add_test
    >
    

    2. 不同保存策略的数据删除

    从前面的语法定义中,没有看到指定保留策略的情况,那么如果需要删除某个保存策略的数据,应该怎样?

    > insert add_test,name=YiHui,phone=110 boy=true,age=19i,user_id=2
    > insert into "1D" add_test,name=YiHui,phone=110 boy=true,age=19i,user_id=1
    > select * from add_test
    name: add_test
    time                age boy  name  phone user_id
    ----                --- ---  ----  ----- -------
    1564483471390538399 19  true YiHui 110   2
    > select * from "1D".add_test
    name: add_test
    time                age boy  name  phone user_id
    ----                --- ---  ----  ----- -------
    1564483483748916258 19  true YiHui 110   1
    
    > delete from add_test where "name"='YiHui'
    > select * from add_test
    > select * from "1D".add_test
    > 
    

    执行上面的case之后,发现根据tag进行删除时,默认策略,和"1D"保存策略中的数据都被删除掉了

    下面是另外一个验证

    > select * from add_test;
    name: add_test
    time                age boy  name  phone user_id
    ----                --- ---  ----  ----- -------
    1564483778197609864 19  true YiHui 110   1
    > insert into "2_h"  add_test,name=YiHui,phone=110 boy=true,age=19i,user_id=1
    > select * from "2_h".add_test;
    name: add_test
    time                age boy  name  phone user_id
    ----                --- ---  ----  ----- -------
    1564483793280811751 19  true YiHui 110   1
    > delete from add_test where time=1564483793280811751
    > select * from "2_h".add_test;
    > select * from add_test;
    name: add_test
    time                age boy  name  phone user_id
    ----                --- ---  ----  ----- -------
    1564483778197609864 19  true YiHui 110   1
    

    我们在"2_h"这个策略中新增了一条数据,直接根据时间进行删除,当前的策略下的数据没有影响,"2_h"策略中刚添加的数据被删除掉了

    II. 其他

    0. 系列博文

    参考博文

    1. 一灰灰Blog: https://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    3. 扫描关注

    一灰灰blog

    展开全文
  • 深入理解数据压缩与重复数据删除

    万次阅读 热门讨论 2011-04-14 20:29:00
    数据压缩与重复数据删除两种技术有何区别与联系呢?实际又该如何正确应用呢?笔者之前对数据压缩原理和技术没有研究,因此做了点功课,查阅整理了相关资料,并与重复数据删除技术进行对比分析。

    [导读] 数据压缩与重复数据删除两种技术有何区别与联系呢?实际中又该如何正确应用呢?笔者之前对数据压缩原理和技术没有研究,因此做了点功课,查阅整理了相关资料,并与重复数据删除技术进行对比分析。

     

    面对数据的急剧膨胀,企业需要不断购置大量的存储设备来应对不断增长的存储需求。然而,单纯地提高存储容量,这似乎并不能从根本解决问题。首先,存储设备的采购预算越来越高,大多数企业难以承受如此巨大的开支。其次,随着数据中心的扩大,存储管理成本、占用空间、制冷能力、能耗等也都变得越来越严重,其中能耗尤为突出。再者,大量的异构物理存储资源大大增加了存储管理的复杂性,容易造成存储资源浪费和利用效率不高。因此,我们需要另辟蹊径来解决信息的急剧增长问题,堵住数据“井喷”。高效存储理念正是为此而提出的,它旨在缓解存储系统的空间增长问题,缩减数据占用空间,简化存储管理,最大程度地利用已有资源,降低成本。目前业界公认的五项高效存储技术分别是数据压缩、重复数据删除、自动精简配置、自动分层存储和存储虚拟化。目前,数据压缩和重复数据删除是实现数据缩减的两种关键技术。简而言之,数据压缩技术通过对数据重新编码来降低冗余度,而重复数据删除技术侧重于删除重复的数据块,从而实现数据容量缩减的目的。

     

    数据压缩[1][2]
    数据压缩的起源可以追溯到信息论之父香农(Shannon)在1947年提出的香农编码。1952年霍夫曼(Huffman)提出了第一种实用性的编码算法实现了数据压缩,该算法至今仍在广泛使用。1977年以色列数学家Jacob Ziv 和Abraham Lempel提出了一种全新的数据压缩编码方式,Lempel-Ziv系列算法(LZ77和LZ78,以及若干变种)凭借其简单高效等优越特性,最终成为目前主要数据压缩算法的基础。LZ系列算法属于无损数据压缩算法范畴,采用词曲编码技术实现,目前主要包括LZ77、LZSS、LZ78和LZW四种主流算法。可以归纳为两类:
    第一类词典法的想法是企图查找正在压缩的字符序列是否在前面的输入数据中出现过,如果是,则用指向早期出现过的字符串的“指针”替代重复的字符串。这种编码思想如图1所示。这里的“词典”是隐含的,指用以前处理过的数据。这类编码中的所有算法都是以Abraham Lempel和Jakob Ziv在1977年开发和发表的算法(称为LZ77算法)为基础。此算法的一个改进算法是由Storer和Szymanski在1982年开发的,称为LZSS算法。

    图1 第一类词典法编码概念

     

    第二类算法的想法是企图从输入的数据中创建一个“短语词典(dictionary of the phrases)”。编码数据过程中当遇到已经在词典中出现的“短语”时,编码器就输出这个词典中的短语的“索引号”,而不是短语本身。这个概念如图2所示。A.Lempel和J.Ziv在1978年首次发表了介绍这种编码方法的文章,称为LZ78。在他们的研究基础上,Terry A.Welch在1984年发表对这种编码算法进行了改进的文章,并首先在高速硬盘控制器上应用了这种算法。因此后来把这种编码方法称为LZW(Lempel-Ziv Walch)压缩编码。

    图2 第二类词典法编码概念

    Lempel-Ziv系列算法的基本思路是用位置信息替代原始数据从而实现压缩,解压缩时则根据位置信息实现数据的还原,因此又被称作"字典式"编码。目前存储应用中压缩算法的工业标准(ANSI、QIC、IETF、FRF、TIA/EIA)是LZS(Lempel-Ziv-Stac),由Stac公司提出并获得专利,当前该专利权的所有者是Hifn, Inc.。数据压缩的应用可以显著降低待处理和存储的数据量,一般情况下可实现2:1 ~ 3:1的压缩比。

     

    LZ77算法[3]
    1977年,Jacob Ziv和Abraham Lempel描述了一种基于滑动窗口缓存的技术,该缓存用于保存最近刚刚处理的文本(J. Ziv and A. Lempel, “A Universal Algorithm for Sequential Data Compression”, IEEE Transaction on Information Theory, May 1977)。这个算法一般称为LZ77。LZ77和它的变体发现,在正文流中词汇和短语(GIF中的图像模式)很可能会出现重复。当出现一个重复时,重复的序列可以用一个短的编码来代替。压缩程序扫描这样的重复,同时生成编码来代替重复序列。随着时间的过去,编码可以重用来捕获新的序列。算法必须设计成解压程序能够在编码和原始数据序列推导出当前的映射。

    图3 LZ77算法示意图


    LZ77(及其变体)的压缩算法使用了两个缓存。滑动历史缓存包含了前面处理过的N个源字符,前向缓存包含了将要处理的下面L个字符。算法尝试将前向缓存开始的两个或多个字符与滑动历史缓存中的字符串相匹配。如果没有发现匹配,前向缓存的第一个字符作为9 bit的字符输出并且移入滑动窗口,滑动窗口中最久的字符被移出。如果找到匹配,算法继续扫描以找出最长的匹配。然后匹配字符串作为三元组输出(指示标记、指针和长度)。对于K个字符的字符串,滑动窗口中最久的K个字符被移出,并且被编码的K个字符被移入窗口。
    尽管LZ77是有效的,对于当前的输入情况也是合适的,但是存在一些不足。算法使用了有限的窗口在以前的文本中查找匹配,对于相对于窗口大小来说非常长的文本块,很多可能的匹配就会被丢掉。窗口大小可以增加,但这会带来两个损失:(1)算法的处理时间会增加,因为它必须为滑动窗口的每个位置进行一次与前向缓存的字符串匹配的工作;(2)<指针>字段必须更长,以允许更长的跳转。

     

    LZSS算法[4]
    LZS算法基于LZ77实现,主要由两部分构成,滑窗(Sliding Window)和自适应编码(Adaptive Coding)。压缩处理时,在滑窗中查找与待处理数据相同的块,并用该块在滑窗中的偏移值及块长度替代待处理数据,从而实现压缩编码。如果滑窗中没有与待处理数据块相同的字段,或偏移值及长度数据超过被替代数据块的长度,则不进行替代处理。LZS算法的实现非常简洁,处理比较简单,能够适应各种高速应用。

    LZ77通过输出真实字符解决了在窗口中出现没有匹配串的问题,但这个解决方案包含有冗余信息。冗余信息表现在两个方面,一是空指针,二是编码器可能输出额外的字符,这种字符可能包含在下一个匹配串中。LZSS算法以比较有效的方法解决这个问题,它的思想是如果匹配串的长度比指针本身的长度长就输出指针,否则就输出真实字符。由于输出的压缩数据流中包含有指针和字符本身,为了区分它们就需要有额外的标志位,即ID位。
    在相同的计算环境下,LZSS算法可获得比LZ77更高的压缩比,而译码同样简单。这也就是为什么这种算法成为开发新算法的基础。许多后来开发的文档压缩程序都使用了LZSS的思想,例如PKZip,ARJ,LHArc和ZOO等等,其差别仅仅是指针的长短、窗口的大小等有所不同。LZSS同样可以和熵编码联合使用,例如ARJ就与霍夫曼编码联用,而PKZip则与Shannon-Fano联用,它的后续版本也采用霍夫曼编码。

     

    LZ78算法[5]
    LZ78的编码思想是不断地从字符流中提取新的“缀-符串(String)”(通俗地理解为新“词条”),然后用“代号”也就是码字(Code word)表示这个“词条”。这样一来,对字符流的编码就变成了用码字(Code word)去替换字符流,生成码字流,从而达到压缩数据的目的。与LZ77相比,LZ78的最大优点是在每个编码步骤中减少了缀-符串(String)比较的数目,而压缩率与LZ77类似。

     

    LZW算法[6]
    LZW编码是围绕称为词典的转换表来完成的。这张转换表用来存放称为前缀(Prefix)的字符序列,并且为每个表项分配一个码字(Code word),或者叫做序号。这张转换表实际上是把8位ASCII字符集进行扩充,增加的符号用来表示在文本或图像中出现的可变长度ASCII字符串。扩充后的代码可用9位、10位、11位、12位甚至更多的位来表示。Welch的论文中用了12位,12位可以有4096个不同的12位代码,这就是说,转换表有4096个表项,其中256个表项用来存放已定义的字符,剩下3840个表项用来存放前缀(Prefix)。
    LZW编码器(软件编码器或硬件编码器)就是通过管理这个词典完成输入与输出之间的转换。LZW编码器的输入是字符流(Charstream),字符流可以是用8位ASCII字符组成的字符串,而输出是用n位(例如12位)表示的码字流(Codestream),码字代表单个字符或多个字符组成的字符串。
    LZW算法得到普遍采用,它的速度比使用LZ77算法的速度快,因为它不需要执行那么多的缀-符串比较操作。对LZW算法进一步的改进是增加可变的码字长度,以及在词典中删除老的缀-符串。在GIF图像格式和UNIX的压缩程序中已经采用了加上这些改进措施之后的LZW算法。LZW算法取得了专利,专利权的所有者是美国的一个大型计算机公司—Unisys(优利系统公司),除了商业软件生产公司之外,可以免费使用LZW算法。

     

    重复数据删除[1][7][8]
    在备份、归档等实际的存储实践中,人们发现有大量的重复数据块存在,既占用了传输带宽又消耗了相当多的存储资源:有些新文件只是在原有文件上作了部分改动,还有某些文件存在着多份拷贝,如果对所有相同的数据块都只保留一份实例,实际存储的数据量将大大减少--这就是重复数据删除技术的基础。这一做法最早由普林斯顿大学李凯教授(DataDomain的三位创始人之一)提出,称之为全局压缩(Global Compression),并作为容量优化存储推广到商业应用。
    重复数据删除(Deduplication)是一种数据缩减技术,可对存储容量进行有效优化。它通过删除数据集中重复的数据,只保留其中一份,从而消除冗余数据,其原理如图4所示。Dedupe技术可以有效提高存储效率和利用率,数据可以缩减到原来的1/20~1/50。这种技术可以很大程度上减少对物理存储空间的需求,减少传输过程中的网络带宽,有效节约设备采购与维护成本。同时它也是一种绿色存储技术,能有效降低能耗。

    图4 重复数据删除技术原理

     

    Dedupe按照消重的粒度可以分为文件级和数据块级。文件级的dedupe技术也称为单一实例存储(SIS, Single Instance Store),数据块级的重复数据删除,其消重粒度更小,可以达到4-24KB之间。显而易见,数据块级可以提供更高的数据消重率,因此目前主流的 dedupe产品都是数据块级的。Dedupe将文件分割成定长或变长的数据块,采用MD5/SHA1等Hash算法为数据块计算指纹(FP, Fingerprint)。可以同时使用两种及以上hash算法计算数据指纹,以获得非常小的数据碰撞发生概率。具有相同指纹的数据块即可认为是相同的数据块,存储系统中仅需要保留一份。这样,一个物理文件在存储系统就对应一个逻辑表示,由一组FP组成的元数据。当进行读取文件时,先读取逻辑文件,然后根据FP序列,从存储系统中取出相应数据块,还原物理文件副本。 

    Dedupe技术可以帮助众多应用降低数据存储量,节省网络带宽,提高存储效率,减小备份窗口,有效节省成本。Dedupe技术目前最成功的应用领域是数据备份、容灾和归档系统,然而事实上dedupe技术可以用于很多场合,包括在线数据、近线数据、离线数据存储系统,可以在文件系统、卷管理器、NAS、SAN中实施。Dedupe也可以用数据传输与同步,作为一种数据压缩技术可用于数据打包。为什么dedupe技术最成功的应用是数据备份领域,而其他领域应用很少呢?这主要由两方面的原因决定的,一是数据备份应用对数据进行多次备份后,存在大量重复数据,非常适合这种技术。二是dedupe技术的缺陷,主要是数据安全、性能。Dedupe使用hash指纹来识别相同数据,存在产生数据碰撞并导致数据不一致性的可能性。Dedupe需要进行数据块切分、数据块指纹计算和数据块检索,消耗可观的系统资源,对存储系统性能产生影响。 

    Dedupe的衡量维度主要有两个,即重复数据删除率(Deduplication ratios)和性能。Dedupe性能取决于具体实现技术,而重复数据删除率则由数据自身的特征和应用模式所决定,目前各存储厂商公布的重复数据删除率从20:1到500:1不等。对何种数据进行消重,时间数据还是空间数据,全局数据还是局部数据?何时进行消重,在线还是离线?在何处进行消重,源端还是目标端?如何进行消重?实际应用Dedupe技术时应该考虑各种因素,因为这些因素会直接影响其性能和效果。另外值得一得的是,hash碰撞问题现在还没有根本的解决方法,因此对于关键业务数据应该慎重考虑应用dedupe技术。 

    存储系统的重复数据删除过程一般是这样的:首先将数据文件分割成一组数据块,为每个数据块计算指纹,然后以指纹为关键字进行Hash查找,匹配则表示该数据块为重复数据块,仅存储数据块索引号,否则则表示该数据块是一个新的唯一块,对数据块进行存储并创建相关元信息。这样,一个物理文件在存储系统就对应一个逻辑表示,由一组FP组成的元数据。当进行读取文件时,先读取逻辑文件,然后根据FP序列,从存储系统中取出相应数据块,还原物理文件副本。从如上过程中可以看出,Dedupe的关键技术主要包括文件数据块切分、数据块指纹计算和数据块检索。 

    重复数据删除技术的关键在于数据块"指纹"的生成和鉴别。数据块"指纹"是鉴别数据块是否重复的依据,如果不同数据块的"指纹"相同,就会造成内容丢失,产生不可恢复的严重后果。在目前的实际应用中,一般都选择MD5或SHA-1等标准杂凑(hash)算法生成的数据块的摘要(digest)作为"指纹",以区分不同数据块间存在的差异,从而保证不同数据块之间不会发生冲突。但是,MD5,SHA-1等算法的计算过程非常复杂,纯软件计算很难满足存储应用的性能需求,"指纹"的计算往往成为重复数据删除应用的性能瓶颈。

     

    数据压缩与重复数据删除对比分析
    数据压缩和重复数据删除技术都着眼于减少数据量,其差别在于数据压缩技术的前提是信息的数据表达存在冗余,以信息论研究作为基础;而重复数据删除的实现依赖数据块的重复出现,是一种实践性技术。然而,通过上面的分析我们发现,这两种技术在本质上却是相同的,即通过检索冗余数据并采用更短的指针来表示来实现缩减数据容量。它们的区别关键在于,消除冗余范围不同,发现冗余方法不同,冗余粒度不同,另外在具体实现方法有诸多不同。
    (1)消除冗余范围
    数据压缩通常作用于数据流,消除冗余范围受到滑动窗口或缓存窗口的限制。由于考虑性能因素,这个窗口通常是比较小的,只能对局部数据产生作用,对单个文件效果明显。重复数据删除技术先对所有数据进行分块,然后以数据块为单位在全局范围内进行冗余消除,因此对包含众多文件的全局存储系统,如文件系统,效果更加显著。如果把数据压缩应用于全局,或者把重复数据删除应用于单个文件,则数据缩减效果要大大折扣。

    (2)发现冗余方法
    数据压缩主要通过串匹配来检索相同数据块,主要采用字符串匹配算法及其变种,这是精确匹配。重复数据删除技术通过数据块的数据指纹来发现相同数据块,数据指纹采用hash函数计算获得,这是模糊匹配。精确匹配实现较为复杂,但精度高,对细粒度消除冗余更为有效;模糊匹配相对简单许多,对大粒度的数据块更加适合,精度方面不够。

    (3)冗余粒度
    数据压缩的冗余粒度会很小,可以到几个字节这样的小数据块,而且是自适应的,不需要事先指定一个粒度范围。重复数据删除则不同,数据块粒度比较大,通常从512到8K字节不等。数据分块也不是自适应的,对于定长数据块需要事先指定长度,变长数据分块则需要指定上下限范围。更小的数据块粒度会获得更大的数据消冗效果,但计算消耗也更大。

    (4)性能瓶颈
    数据压缩的关键性能瓶颈在于数据串匹配,滑动窗口或缓存窗口越大,这个计算量就会随之大量增加。重复数据删除的性能瓶颈在于数据分块与数据指纹计算,MD5/SHA-1等hash函数的计算复杂性都非常高,非常占用CPU资源。另外,数据指纹需要保存和检索,通常需要大量内存来构建hash表,如果内存有限则会对性能产生严重影响。

    (5)数据安全
    这里的数据压缩都是无损压缩,不会发生数据丢失现象,数据是安全的。重复数据删除的一个问题是,利用hash产生的数据块指纹可能会产生的碰撞,即两个不同的数据块生成了相同的数据指纹。这样,就会造成一个数据块丢失的情况发生,导致数据发生破坏。因此,重复数据删除技术存在数据安全隐患。

    (6)应用角度
    数据压缩直接对流式数据进行处理,不需要事先对全局信息进行分析统计,可以很好地利用流水线或管道方式与其他应用结合使用,或以带内方式透明地作用于存储系统或网络系统。重复数据删除则需要对数据进行分块处理,需要对指纹进行存储和检索,需要对原先物理文件进行逻辑表示。如果现有系统要应用这种技术,则需要对应用进行修改,难以做到透明实现。目前重复数据删除并不是一个通常功能,而更多地以产品形态出现,如存储系统、文件系统或应用系统。因此,数据压缩是一种标准功能,而重复数据删除现在还没有达到这种标准,应用角度来看,数据压缩更为简单。

     

    珠联璧合
    数据压缩与重复数据删除两种技术具有不同层面的针对性,并能够结合起来使用,从而实现更高的数据缩减比例。值得一提的是,如果同时应用数据压缩和重复数据删除技术,为了降低对系统的处理需求和提高数据压缩比率,通常需要先应用数据删除技术,然后再使用数据压缩技术进一步降低"结构图"和基本数据块的体积。如果顺序颠倒会出现什么样的结果呢?压缩会对数据进行重新编码,从而破坏了数据原生的冗余结构,因此再应用重复数据删除效果则会大打折扣,而且消耗时间也更多。而先执行重复数据删除则不同,它首先消除了冗余数据块,然后应用数据压缩对唯一副本数据块进行再次压缩。这样,两种技术的数据缩减作用得到叠加,而且数据压缩的消耗时间大大降低。因此,先去重后压缩,可以获得更高的数据压缩率和性能。这里我们以gzip和作者自己实现的开源小软件deduputil[8]来验证这个应用经验。

    原始数据:linux-2.6.37内核源码,du -h的容量为1107724KB,约1081.8MB。
    对linux-2.6.37目录执行time gzip -c -r linux-2.6.37 > linux-2.6.37.gz,压缩得到linux-2.6.37.gz,容量约为264MB,消耗时间152.776s;
    对linux-2.6.37目录执行time dedup -c -b 4096 linux-2.6.37.ded linux-2.6.37,去重得到linux-2.6.37.ded,容量约为622MB,消耗时间28.890s;
    对linux-2.6.37.gz执行time dedup -c -b 4096 linux-2.6.37.gz.ded linux-2.6.37.gz,去重得到linux-2.6.37.gz.ded,容量约为241MB,消耗时间7.216;
    对linux-2.6.37.ded执行time gzip -c linux-2.6.37.ded > linux-2.6.37.ded.gz,压缩得到linux-2.6.36.ded.gz,容量约为176MB,消耗时间38.682;
    经过实验可得,dedup + gzip得到的linux-2.6.37.ded.gz容量为176MB,消耗时间为67.572秒;gzip + dedup得到的linux-2.6.37.gz.ded容量为241MB,消耗时间为159.992秒。实验数据进一步验证了上述的分析,先数据去重再数据压缩,能够获得更高的数据压缩率和性能。

     

    参考阅读
    1 数据缩减技术 http://tech.watchstor.com/management-116492.htm
    2 数据压缩原理 http://jpkc.zust.edu.cn/2007/dmt/course/MMT03_05_1.htm
    3 LZ77算法 http://jpkc.zust.edu.cn/2007/dmt/course/MMT03_05_2.htm
    4 LZSS算法 http://jpkc.zust.edu.cn/2007/dmt/course/MMT03_05_3.htm
    5 LZ78算法 http://jpkc.zust.edu.cn/2007/dmt/course/MMT03_05_4.htm
    6 LZW算法 http://jpkc.zust.edu.cn/2007/dmt/course/MMT03_05_5.htm
    7 重复数据删除技术研究 http://blog.csdn.net/liuben/archive/2010/08/21/5829083.aspx
    8 高效存储技术研究 http://blog.csdn.net/liuben/archive/2010/12/08/6064045.aspx
    8 Deduputil http://sourceforge.net/projects/deduputil/

    展开全文
  • 数据结构实践项目——链表

    千次阅读 2015-09-12 11:26:19
    本组项目针对《数据结构基础系列(2):线性表》课程第8-15节 8. 线性表的链式存储 9. 建立单链表 10. 单链表基本操作的实现 11. 单链表应用举例 12. 双链表 13. 循环链表 14. 线性表的应用 15. 有序 【项目...

    本组项目针对《数据结构基础系列(2):线性表》课程第8-15节
    8. 线性表的链式存储
    9. 建立单链表
    10. 单链表基本操作的实现
    11. 单链表应用举例
    12. 双链表
    13. 循环链表
    14. 线性表的应用
    15. 有序表

    【项目1 - 建立单链表】
      定义单链表存储结构,用头插法和尾插法建立单链表,并显示建立好以后的结果。
      请在下面代码的基础上开展工作:

    #include <stdio.h>
    #include <malloc.h>
    typedef int ElemType;
    typedef struct LNode        //定义单链表结点类型
    {
        ElemType data;
        struct LNode *next;     //指向后继结点
    } LinkList;
    
    void CreateListF(LinkList *&L,ElemType a[],int n);//头插法建立单链表
    void CreateListR(LinkList *&L,ElemType a[],int n);//尾插法建立单链表
    void DestroyList(LinkList *&L); //销毁单链表
    void DispList(LinkList *L)  //输出单链表
    
    int main()
    {
        LinkList *L1, *L2;
        ElemType a[8]= {7, 9, 8, 2, 0, 4, 6, 3};
        CreateListF(L1, a, 8);
        printf("头插法建表结果:");
        DispList(L1);
        CreateListR(L2, a, 6);
        printf("尾插法建表结果:");
        DispList(L2);
        DestroyList(L1);
        DestroyList(L2);
        return 0;
    }
    //在下面写自定义函数(实现相关算法)的代码

    参考解答

    【项目2 - 建设“单链表”算法库】
      按照“0207将算法变程序”部分建议的方法,建设自己的专业基础设施算法库。这一周,建的是单链表的算法库。
      算法库包括两个文件:
      头文件:linklist.h,包含定义顺序表数据结构的代码、宏定义、要实现算法的函数的声明;
      源文件:linklist.cpp,包含实现各种算法的函数的定义
      
      请采用程序的多文件组织形式,建立如上的两个文件,另外再建立一个源文件(如main.cpp),编制main函数,完成相关的测试工作。
      测试工作可以采用“渐进”的思路,每次涉及的函数应该尽可能少。
      例如,首先设计测试函数,可以涉及初始化线性表、销毁线性表、输出线性表、插入数据元素对应的函数,设计的测试函数可以是

    #include "linklist.h"
    int main()
    {
        LinkList *L;
        InitList(L);
        ListInsert(L, 1, 15);
        ListInsert(L, 1, 10);
        ListInsert(L, 1, 5);
        ListInsert(L, 1, 20);
        DispList(L);
        DestroyList(L);
        return 0;
    }

    参考解答

    【项目3 - 单链表应用】
      完成下面的应用时,除项目中给出的特殊要求,其余工作均可利用项目2完成的算法支持。
      1、设计一个算法,将一个带头结点的数据域依次为a1,a2,…,an(n≥3)的单链表的所有结点逆置,即第一个结点的数据域变为an,…,最后一个结点的数据域为a1。实现这个算法,并完成测试。

    提示:实现算法时,可以设计下面的函数:void Reverse(LinkList *&L)

      2、已知L1和L2分别指向两个单链表的头结点,且已知其长度分别为m、n,请设计算法将L2连接到L1的后面。实现这个算法,完成测试,并分析这个算法的复杂度。

    提示:实现算法时,可以设计下面的函数:void Link(LinkList &L1, LinkList &L2)

      3、设计一个算法,判断单链表L是否是递增的。实现这个算法,并完成测试。

    [参考解答]

    【项目4 - 建设双链表算法库(选做)】
      算法库包括两个文件:
      头文件:dlinklist.h,包含定义顺序表数据结构的代码、宏定义、要实现算法的函数的声明;
      源文件:dlinklist.cpp,包含实现各种算法的函数的定义
      请采用程序的多文件组织形式,建立如上的两个文件,另外再建立一个源文件(如main.cpp),编制main函数,完成相关的测试工作。
    参考解答

    【项目5 - 猴子选大王】
      一群猴子,编号是1,2,3 …m,这群猴子(m个)按照1-m的顺序围坐一圈。从第1只开始数,每数到第n个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子为大王。输入m和n,输出为大王的猴子是几号。

    提示:
    (1)链表解法:可以用一个循环单链表来表示这一群猴子。表示结点的结构体中有两个成员:一个保存猴子的编号,一个为指向下一个人的指针,编号为m的结点再指向编号为1的结点,以此构成环形的链。当数到第n个时,该结点被删除,继续数,直到只有一个结点。
    (2)使用结构数组来表示循环链:结构体中设一个成员表示对应的猴子是否已经被淘汰。从第一个人未被淘汰的数起,每数到n时,将结构中的标记改为0,表示这只猴子已被淘汰。当数到数组中第m个元素后,重新从第一个数起,这样循环计数直到有m-1被淘汰。
    (3)该问题为计算机科学中的经典问题,很多实际的问题可以抽象到这种模型上来。感兴趣的同学请搜索“约瑟夫问题”。
    参考解答

    【项目5- 循环双链表应用】
      设非空线性表ha和hb都用带头节点的循环双链表表示。设计一个算法Insert(ha,hb,i)。其功能是:i=0时,将线性表hb插入到线性表ha的最前面;当i>0时,将线性表hb插入到线性表ha中第i个节点的后面;当i大于等于线性表ha的长度时,将线性表hb插入到线性表ha的最后面。
      请在实现算法时,除项目中给出的特殊要求,其余工作均可利用项目4完成的算法支持。
    参考解答

    【项目6 - 多项式求和】
      用单链表存储一元多项式,并实现两个多项式的加法。

    提示:
    1、存储多项式的数据结构
      多项式的通式是pn(x)=anxn+an1xn1+...+a1x+a0。n次多项式共有n+1项。直观地,可以定义一个数组来存储这n+1个系数。以多项式p(x)=3.4x109.6x8+7.2x2+x为例,存储这个多项式的数组如下图:
    这里写图片描述
      可以看出,这种方案适合对某些多项式的处理。但是,在处理一些次数高但项数少的多项式时,存在浪费空间的现象,会有很多闲置的0。
      可以使用如下定义的单链表结构存储多项式:链表中的每一个结点是多项式中的一项,结点的数据域包括指数和系数两部分,由指针域连接起多项式中的各项。

    typedef struct pnode //定义单链表结点类型,保存多项式中的一项,链表构成多项式 {
    double coef; //系数,浮点数
    int exp; //指数,正整数*
    struct pnode *next; //指向下一项的指针
    } PolyNode;

      用于表示多项式的链表将如下图所示,在建立多项式的链表时,已经令结点按指数由大到小的顺序排列。
    这里写图片描述

    2、多项式加法在链表存储结构下的实现
      链表存储结构下,多项式加法的实现 在如上定义的单链表存储结构基础上,讨论实现多项式加法的算法。
      两个多项式相加,其规则是对具有相同指数的项,令其系数相加。设两个待相加的多项式的链表的头指针分别为head1(第一个多项式)和head2(第二个多项式),两者的和保存到链表head1中。只需要先将head1和head2链表的首结点作为当前结点(分别用p1和p2指向)开始检测,在遍历链表的过程中,分情况作如下处理:
      (1)若两个多项式中当前结点的指数值相同,则它们的系数相加,结果保存到p1结点,并将p2结点删除。如果相加后的系数不为0,p1指向第一个多项式的下一个结点,准备随后的工作,否则,不保存系数为0的项,将当前p1结点删除。
      (2)当两个多项式中对应结点的指数值不相等时,若p1指向的结点的指数大,则p1简单地指向下一结点即可;而p2指向的结点大时,需要将p2结点插入到p1前,然后p2再重新指回到第二个多项式中的下一结点,继续进行处理。
      (3)检测过程直到其中的任一个链表结束。若p1不为空,第一个多项式中的剩余项已经在链表中,不作处理,如果p2不为空,只需要将p2链接到相加后的第一个多项式末尾。
      上面的讨论假设多项式链表中,已经按指数由大到小排序,在加法之前,采取多种手段保证这一前提成立。

    参考解答

    展开全文
  • Tableau工作表2.1 Tableau工作表2.2 Tableau编辑元数据3. Tableau字段操作3.1 合并字段3.2 字段分层3.3 字段分组3.4 字段拆分3.5 计算字段3.6 集4. Tableau函数与计算4.1 Tableau的运算符4.2 Tableau函数(1)...
  • 数据结构一线性 (顺序、单链表、双链表)

    万次阅读 多人点赞 2018-07-17 16:38:57
    转载请标明出处: ...本文出自:【openXu的博客】 1、线性表及其逻辑结构   线性表是最简单也是最常用的一种数据...成绩单是一个线性表,表中每一行是一个数据元素,每个数据元素又由学号、姓名、成绩等数据项...
  • 重复数据删除技术概述

    千次阅读 2014-10-19 21:59:04
    重复数据删除技术概述 一、 重复数据删除的分类 1. 源端重复数据删除和目标端重复数据删除 源端消重在数据源进行,传输的是已经消重后的数据,能够节省网络带宽,但会占用大量源端系统资源。 目标端消重发生在...
  • 数据结构实践——链表:多项式求和

    万次阅读 多人点赞 2015-09-12 11:31:31
    本文针对数据结构基础系列网络课程(2):线性表的实践项目。【项目6 - 多项式求和】  用单链表存储一元多项式,并实现两个多项式的加法。 提示: 1、存储多项式的数据结构 多项式的通式是pn(x)=anxn+an−1...
  • 使用切片器实现数据透视图和透视的动态效果。初步介绍度量值以及度量值在透视和透视图的作用。
  • 这个系列将罗列自己以前的笔记以及深入MySQL一些高级用法。打算先讲很多人关注的使用方式(增删改查以及其优化),然后就讲数据库和的操作(很多我们学习忽略的地方),接着就是存储引擎还有更高级的查询等等。OK...
  • LINQ和 LINQ to SQL 都已经不是一个新事物了,但是我接触的比较晚,本着绝知此事要躬...1. 从CUD开始,如何使用LINQ to SQL插入、修改、删除数据 2. 查询 使用LINQ to SQL做简单查询 3. 查询 延迟加载与立
  • 在各种应用软件,客户总是希望看到自己操作关键业务的历史数据(更或者是将来的历史数据,...如果我们需要新增一条记录,并且还需要在不同的表中新增对应的详细记录并且是一对多的关系(如报价,我们需要储存报价
  • 数据库创建成功后可在该数据库创建数据表(简称为)存储数据。请注意:在操作数据表之前应使用“USE 数据库名;”指定操作是在哪个数据库进行先关操作,否则会抛出“No database selected”错误。
  • 为什么删除数据还可以被恢复?

    千次阅读 2018-09-19 14:47:35
    我们大家可能都遇到过这样的事情,上午刚刚清空垃圾站,下午却突然...不过我们的硬盘发生文件被误删除,分区丢失,病毒破坏等情况,通过一些数据恢复软件自己就能够解决。下面就详细介绍一下数据恢复的原理和一些常...
  • 数据结构实践项目——顺序

    千次阅读 2015-09-07 14:59:34
    【项目1 - 顺序的基本运算】  领会“0207将算法变程序”部分建议的方法,将顺序部分的算法变成程序。  实现顺序基本运算有算法(对应视频0204创建线性表的实现和0205顺序基本运算的实现),依据“最小化...
  • BW系列数据

    千次阅读 2016-12-14 09:53:52
    BW系列数据
  • Oracle 空间与数据文件

    万次阅读 热门讨论 2010-05-20 16:46:00
    -Oracle 表空间与数据文件--==============================/*一、概念 表空间:是一个或多个数据文件的逻辑集合 表空间逻辑存储对象:永久段-->如表与索引 临时段-->如临时表数据与排序段 
  • 重温数据结构:哈希 哈希函数 哈希

    万次阅读 多人点赞 2016-10-27 00:49:30
    点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ ...在某种程度上,散列是与排序相反的一种操作,排序是将集合的元素按照某种方式比如字典顺序排列在一起,而散列通过计算哈希值,打破元素之间原
  • 工作系列(4.1)-Activity结构介绍

    千次阅读 2018-11-30 14:50:37
    文章目录Activiti结构介绍通用信息1)ACT_GE_BYTEARRAY 资源2)ACT_GE_PROPERTY 属性资源信息1)ACT_RE_DEPLOYMENT 部署信息2)ACT_RE_MODEL 模型信息2)ACT_RE_PRODEF 流程信息用户信息1)ACT_ID_GROUP 用户组...
  • 等等,我还在苦苦学习这些之,大数据时代悄然到来,并迅速改变着各行各业。如今,十年风云际会,大数据早已成了行业绕不开的话题,这其中我们或多或少会接触到很多新兴的概念,例如数据湖、数据中台等,通过一些...
  • 数据表设计原则

    千次阅读 2014-11-06 09:56:10
    不同组件间所对应的数据库之间的关联应尽可能减少,如果不同组件间的需要外键关联也尽量不要创建外键关联,而只是记录关联的一个主键,确保组件对应的之间的独立性,为系统或结构的重构提供可能性。...
  • 重复数据删除技术简介

    千次阅读 2013-05-29 09:55:02
    0.绪论  一般情况下,数据中心每周需要将主存储设备的所有数据备份到辅存储设备上,并保存数月时间,我们称之为全备份。另外,数据中心可能每天还需做一次增量备份,只备份当天改变的数据。... 从重复数据删除
  • 重复数据删除技术的研究与分析

    千次阅读 2012-06-26 11:21:33
    重复数据删除技术的 研究与分析   摘要 基于磁盘的重删存储已经作为企业数据保护中新一代存储兴起发展并将代替磁带库的存储形式。数据重删技术删除冗余的数据并将数据压缩为一种高紧凑的格式,从而节省...
  • 这一系列博文将介绍一下机器学习数据预处理问题,以UCI数据集为例详细介绍缺失值处理、连续特征离散化,特征归一化及离散特征的编码等问题,同时会附上处理的Matlab程序代码,这篇博文先介绍缺失值的处理,要点...
  • 【SpringBoot2.0系列01】初识SpringBoot 【SpringBoot2.0系列02】SpringBoot之使用Thymeleaf视图模板 【SpringBoot2.0系列03】SpringBoot之使用freemark视图模板 【SpringBoot2.0系列04】SpringBoot之使用JPA完成...
  • 文章开始之前,先把重要的事情说三遍,不要随便用‘truncate’、不要随便用‘truncate’、不要随便用‘truncate’!...昨天晚上的例子,同事加班加累了在测试库测试的时候,没注意在执行truncate
  • oracle不小心删除表恢复方法

    千次阅读 2013-07-15 10:31:12
    使用 Oracle Database 10g的闪回特性,可以毫不费力地恢复被意外删除 以下是一个不该发生却经常发生的情况:用户删除了一个非常重要的—当然是意外地删除—并需要尽快地恢复。(在某些时候,这个不幸的...
  • 【稀饭】react native 实战系列教程之数据存储

    千次阅读 热门讨论 2016-11-03 11:46:45
    概述在开发一款APP,对于数据的存储是在正常不过了,在此之前,【稀饭】这个应用还没有用到存储数据的地方,为了学习研究React Native的数据存储,打算给应用增加【我的收藏】和【观看历史】这两个功能。接下来,...
  • 数据结构

    万次阅读 2018-02-07 00:47:38
    1. 什么是数据结构算法+数据结构=程序设计 ...或者是同一个作者写的,由此我们可以建立一张按课程号顺序排列的课程和两张按类别和作者名顺序排列的索引,如下图所示,这样我们就可以按课程号

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 163,655
精华内容 65,462
关键字:

当删除工作表中的数据系列时