排序、去重、采样、数据变换

您可以对DataFrame对象执行排序、去重、采样、数据变换操作。

前提条件

您需要提前完成以下步骤,用于操作本文中的示例:

  • 准备示例表pyodps_iris,详情请参见Dataframe数据处理

  • 创建DataFrame。

    from odps.df import DataFrame
    iris = DataFrame(o.get_table('pyodps_iris'))

排序

排序操作只能作用于Collection。

  • 调用sort或者sort_values方法进行排序。

    >>> iris.sort('sepalwidth').head(5)
       sepallength  sepalwidth  petallength  petalwidth             name
    0          5.0         2.0          3.5         1.0  Iris-versicolor
    1          6.2         2.2          4.5         1.5  Iris-versicolor
    2          6.0         2.2          5.0         1.5   Iris-virginica
    3          6.0         2.2          4.0         1.0  Iris-versicolor
    4          5.5         2.3          4.0         1.3  Iris-versicolor
  • 设置参数ascending=False;进行降序排列。

    >>> iris.sort('sepalwidth', ascending=False).head(5)
       sepallength  sepalwidth  petallength  petalwidth         name
    0          5.7         4.4          1.5         0.4  Iris-setosa
    1          5.5         4.2          1.4         0.2  Iris-setosa
    2          5.2         4.1          1.5         0.1  Iris-setosa
    3          5.8         4.0          1.2         0.2  Iris-setosa
    4          5.4         3.9          1.3         0.4  Iris-setosa
  • 设置-实现降序排列。

    >>> iris.sort(-iris.sepalwidth).head(5)
       sepallength  sepalwidth  petallength  petalwidth         name
    0          5.7         4.4          1.5         0.4  Iris-setosa
    1          5.5         4.2          1.4         0.2  Iris-setosa
    2          5.2         4.1          1.5         0.1  Iris-setosa
    3          5.8         4.0          1.2         0.2  Iris-setosa
    4          5.4         3.9          1.3         0.4  Iris-setosa
  • 实现多字段排序。

    >>> iris.sort(['sepalwidth', 'petallength']).head(5)
       sepallength  sepalwidth  petallength  petalwidth             name
    0          5.0         2.0          3.5         1.0  Iris-versicolor
    1          6.0         2.2          4.0         1.0  Iris-versicolor
    2          6.2         2.2          4.5         1.5  Iris-versicolor
    3          6.0         2.2          5.0         1.5   Iris-virginica
    4          4.5         2.3          1.3         0.3      Iris-setosa
  • 多字段排序时,如果是升序降序不同,ascending参数可以用于传入一个列表,长度必须等同于排序的字段,它们的值都是BOOLEAN类型。

    >>> iris.sort(['sepalwidth', 'petallength'], ascending=[True, False]).head(5)
       sepallength  sepalwidth  petallength  petalwidth             name
    0          5.0         2.0          3.5         1.0  Iris-versicolor
    1          6.0         2.2          5.0         1.5   Iris-virginica
    2          6.2         2.2          4.5         1.5  Iris-versicolor
    3          6.0         2.2          4.0         1.0  Iris-versicolor
    4          6.3         2.3          4.4         1.3  Iris-versicolor

    下面代码可以实现同样的效果。

    >>> iris.sort(['sepalwidth', -iris.petallength]).head(5)
       sepallength  sepalwidth  petallength  petalwidth             name
    0          5.0         2.0          3.5         1.0  Iris-versicolor
    1          6.0         2.2          5.0         1.5   Iris-virginica
    2          6.2         2.2          4.5         1.5  Iris-versicolor
    3          6.0         2.2          4.0         1.0  Iris-versicolor
    4          6.3         2.3          4.4         1.3  Iris-versicolor
    说明

    由于ODPS要求排序必须指定个数,所以在ODPS后端执行时,会通过options.df.odps.sort.limit指定排序个数,这个值默认是10000。如果需要排序尽量多的数据,可以把这个值设到较大的值。但是,这样可能会导致OOM。

去重

  • 您可以通过以下三种方式调用distinct方法,对Collection进行去重操作。

    • >>> iris[['name']].distinct()
                    name
      0      Iris-setosa
      1  Iris-versicolor
      2   Iris-virginica
    • >>> iris.distinct('name')
                    name
      0      Iris-setosa
      1  Iris-versicolor
      2   Iris-virginica
    • >>> iris.distinct('name', 'sepallength').head(3)
                name  sepallength
      0  Iris-setosa          4.3
      1  Iris-setosa          4.4
      2  Iris-setosa          4.5
  • 您可以调用unique对Sequence进行去重操作,但是调用unique的Sequence不能用在列选择中。

    >>> iris.name.unique()
                  name
    0      Iris-setosa
    1  Iris-versicolor
    2   Iris-virginica

    错误示例如下。

    >>> iris[iris.name, iris.name.unique()]

采样

要对一个Collection的数据采样,可以调用sample方法。PyODPS支持以下四种采样方式:

说明

除了按份数采样外,其余方法如果要在ODPS DataFrame上执行,需要Project支持XFlow,否则,这些方法只能在Pandas DataFrame后端上执行。

  • 按份数采样

    在这种采样方式下,数据被分为parts份,可选择选取的份数序号。

    >>> iris.sample(parts=10)  # 分成10份,默认取第0份。
    >>> iris.sample(parts=10, i=0)  # 手动指定取第0份。
    >>> iris.sample(parts=10, i=[2, 5])   # 分成10份,取第2和第5份。
    >>> iris.sample(parts=10, columns=['name', 'sepalwidth'])  # 根据name和sepalwidth的值做采样。
  • 按权重列采样

    在这种采样方式下,您可以指定权重列和数据条数、采样比例。指定replace参数为True可启用放回采样。

    >>> iris.sample(n=100, weights='sepal_length')
    >>> iris.sample(n=100, weights='sepal_width', replace=True)
  • 分层采样

    在这种采样方式下,您可以指定用于分层的标签列,同时为需要采样的每个标签指定采样比例(frac参数)或条数 (n参数)。暂不支持放回采样。

    >>> iris.sample(strata='category', n={'Iris Setosa': 10, 'Iris Versicolour': 10})
    >>> iris.sample(strata='category', frac={'Iris Setosa': 0.5, 'Iris Versicolour': 0.4})

数据缩放

DataFrame支持通过最大、最小值或平均值、标准差对数据进行缩放。示例数据如下。

name  id  fid
0  name1   4  5.3
1  name2   2  3.5
2  name2   3  1.5
3  name1   4  4.2
4  name1   3  2.2
5  name1   3  4.1
  • 使用min_max_scale方法进行归一化。

    df.min_max_scale(columns=['fid'])
        name  id       fid
    0  name1   4  1.000000
    1  name2   2  0.526316
    2  name2   3  0.000000
    3  name1   4  0.710526
    4  name1   3  0.184211
    5  name1   3  0.684211
  • min_max_scale还支持使用feature_range参数指定输出值的范围。例如,需要使输出值在 (-1, 1) 范围内,示例如下。

    df.min_max_scale(columns=['fid'], feature_range=(-1, 1))
        name  id       fid
    0  name1   4  1.000000
    1  name2   2  0.052632
    2  name2   3 -1.000000
    3  name1   4  0.421053
    4  name1   3 -0.631579
    5  name1   3  0.368421
  • 如果需要保留原始值,可以使用preserve参数。此时,缩放后的数据将会以新增列的形式追加到数据中,列名默认为原列名追加_scaled后缀,该后缀可使用suffix参数更改。

    df.min_max_scale(columns=['fid'], preserve=True)
        name  id  fid  fid_scaled
    0  name1   4  5.3    1.000000
    1  name2   2  3.5    0.526316
    2  name2   3  1.5    0.000000
    3  name1   4  4.2    0.710526
    4  name1   3  2.2    0.184211
    5  name1   3  4.1    0.684211
  • min_max_scale也支持使用group参数指定一个或多个分组列,在分组列中分别取最值进行缩放。

    df.min_max_scale(columns=['fid'], group=['name'])
        name  id       fid
    0  name1   4  1.000000
    1  name1   4  0.645161
    2  name1   3  0.000000
    3  name1   3  0.612903
    4  name2   2  1.000000
    5  name2   3  0.000000

    结果显示,name1name2两组均按组中的最值进行了缩放。

  • 使用std_scale方法可以依照标准正态分布对数据进行调整。std_scale同样支持preserve参数保留原始列以及使用group进行分组。

    df.std_scale(columns=['fid'])
        name  id       fid
    0  name1   4  1.436467
    1  name2   2  0.026118
    2  name2   3 -1.540938
    3  name1   4  0.574587
    4  name1   3 -0.992468
    5  name1   3  0.496234

空值处理

DataFrame支持筛去空值以及填充空值的功能。示例数据如下。

id   name   f1   f2   f3   f4
0   0  name1  1.0  NaN  3.0  4.0
1   1  name1  2.0  NaN  NaN  1.0
2   2  name1  3.0  4.0  1.0  NaN
3   3  name1  NaN  1.0  2.0  3.0
4   4  name1  1.0  NaN  3.0  4.0
5   5  name1  1.0  2.0  3.0  4.0
6   6  name1  NaN  NaN  NaN  NaN
  • 使用dropna,删除subset中包含空值的行。

    df.dropna(subset=['f1', 'f2', 'f3', 'f4'])
       id   name   f1   f2   f3   f4
    0   5  name1  1.0  2.0  3.0  4.0
  • 如果行中包含非空值则不删除,可以使用how=’all’

    df.dropna(how='all', subset=['f1', 'f2', 'f3', 'f4'])
       id   name   f1   f2   f3   f4
    0   0  name1  1.0  NaN  3.0  4.0
    1   1  name1  2.0  NaN  NaN  1.0
    2   2  name1  3.0  4.0  1.0  NaN
    3   3  name1  NaN  1.0  2.0  3.0
    4   4  name1  1.0  NaN  3.0  4.0
    5   5  name1  1.0  2.0  3.0  4.0
  • 使用thresh参数指定行中至少要有多少个非空值。

    df.dropna(thresh=3, subset=['f1', 'f2', 'f3', 'f4'])
       id   name   f1   f2   f3   f4
    0   0  name1  1.0  NaN  3.0  4.0
    2   2  name1  3.0  4.0  1.0  NaN
    3   3  name1  NaN  1.0  2.0  3.0
    4   4  name1  1.0  NaN  3.0  4.0
    5   5  name1  1.0  2.0  3.0  4.0
  • 调用fillna方法,使用常数或已有的列来填充未知值。

    • 使用常数填充未知值。

      df.fillna(100, subset=['f1', 'f2', 'f3', 'f4'])
         id   name     f1     f2     f3     f4
      0   0  name1    1.0  100.0    3.0    4.0
      1   1  name1    2.0  100.0  100.0    1.0
      2   2  name1    3.0    4.0    1.0  100.0
      3   3  name1  100.0    1.0    2.0    3.0
      4   4  name1    1.0  100.0    3.0    4.0
      5   5  name1    1.0    2.0    3.0    4.0
      6   6  name1  100.0  100.0  100.0  100.0
    • 使用一个已有的列填充未知值。

      df.fillna(df.f2, subset=['f1', 'f2', 'f3', 'f4'])
         id   name   f1   f2   f3   f4
      0   0  name1  1.0  NaN  3.0  4.0
      1   1  name1  2.0  NaN  NaN  1.0
      2   2  name1  3.0  4.0  1.0  4.0
      3   3  name1  1.0  1.0  2.0  3.0
      4   4  name1  1.0  NaN  3.0  4.0
      5   5  name1  1.0  2.0  3.0  4.0
      6   6  name1  NaN  NaN  NaN  NaN
  • DataFrame提供了向前、向后填充的功能。您可以按照表格中的取值范围为method参数进行赋值。

    取值

    含义

    bfill、backfill

    向前填充。

    ffill、pad

    向后填充。

    示例

    df.fillna(method='bfill', subset=['f1', 'f2', 'f3', 'f4'])
       id   name   f1   f2   f3   f4
    0   0  name1  1.0  3.0  3.0  4.0
    1   1  name1  2.0  1.0  1.0  1.0
    2   2  name1  3.0  4.0  1.0  NaN
    3   3  name1  1.0  1.0  2.0  3.0
    4   4  name1  1.0  3.0  3.0  4.0
    5   5  name1  1.0  2.0  3.0  4.0
    6   6  name1  NaN  NaN  NaN  NaN
    df.fillna(method='ffill', subset=['f1', 'f2', 'f3', 'f4'])
       id   name   f1   f2   f3   f4
    0   0  name1  1.0  1.0  3.0  4.0
    1   1  name1  2.0  2.0  2.0  1.0
    2   2  name1  3.0  4.0  1.0  1.0
    3   3  name1  NaN  1.0  2.0  3.0
    4   4  name1  1.0  1.0  3.0  4.0
    5   5  name1  1.0  2.0  3.0  4.0
    6   6  name1  NaN  NaN  NaN  NaN

    您也可以使用ffillbfill函数简化代码。ffill等价于fillna(method=’ffill’)bfill等价于fillna(method=’bfill’)