相关文章推荐
力能扛鼎的键盘  ·  Error in ...·  1 年前    · 
另类的开水瓶  ·  Bootstrap ...·  2 年前    · 
冷静的猴子  ·  Citus 分布式 PostgreSQL ...·  2 年前    · 
排序、去重、采样、数据变换

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

您可以对 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()
      0      Iris-setosa
      1  Iris-versicolor
      2   Iris-virginica
    • >>> iris.distinct('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()
    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'])  # 根据namesepalwidth的值做采样。
  • 按权重列采样

    在这种采样方式下,您可以指定权重列和数据条数、采样比例。指定 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

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

  • 使用 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

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