全部产品
Search
文档中心

云原生大数据计算服务 MaxCompute:快速入门

更新时间:Nov 17, 2023

本文为您介绍如何创建和操作DataFrame对象,以及使用DataFrame完成基本的数据处理。

数据准备

本文将以movielens 100K进行举例,下载ml-100k.zip到本地。其中u.user是用户相关的数据,u.item是电影相关的数据,u.data是评分有关的数据。

  1. 创建表:

    • pyodps_ml_100k_users(用户相关的数据)。

      create table if not exists pyodps_ml_100k_users
      (
        user_id    BIGINT COMMENT '用户id',
        age        BIGINT COMMENT '年龄',
        sex        STRING COMMENT '性别',
        occupation STRING COMMENT '职业',
        zip_code   STRING COMMENT '邮编'
      );
    • pyodps_ml_100k_movies(电影相关的数据)。

      CREATE TABLE IF NOT EXISTS pyodps_ml_100k_movies
      (
          movie_id            BIGINT COMMENT '电影 ID'
          ,title              STRING COMMENT '电影标题'
          ,release_date       STRING COMMENT '上映日期'
          ,video_release_date STRING COMMENT '视频发布日期'
          ,IMDb_URL           STRING COMMENT 'IMDb 链接'
          ,unknown            TINYINT COMMENT '未知'
          ,Action             TINYINT COMMENT '动作'
          ,Adventure          TINYINT COMMENT '冒险'
          ,Animation          TINYINT COMMENT '动画'
          ,Children           TINYINT COMMENT '儿童'
          ,Comedy             TINYINT COMMENT '喜剧'
          ,Crime              TINYINT COMMENT '犯罪'
          ,Documentary        TINYINT COMMENT '纪录片'
          ,Drama              TINYINT COMMENT '戏剧'
          ,Fantasy            TINYINT COMMENT '奇幻'
          ,FilmNoir           TINYINT COMMENT '黑色电影'
          ,Horror             TINYINT COMMENT '恐怖'
          ,Musical            TINYINT COMMENT '音乐'
          ,Mystery            TINYINT COMMENT '悬疑'
          ,Romance            TINYINT COMMENT '浪漫'
          ,SciFi              TINYINT COMMENT '科幻'
          ,Thriller           TINYINT COMMENT '惊悚'
          ,War                TINYINT COMMENT '战争'
          ,Western            TINYINT COMMENT '西部'
      );
    • pyodps_ml_100k_ratings(评分有关的数据)。

      CREATE TABLE IF NOT EXISTS pyodps_ml_100k_ratings
      (
          user_id    BIGINT COMMENT '用户id'
          ,movie_id  BIGINT COMMENT '电影id'
          ,rating    BIGINT COMMENT '得分'
          ,timestamp BIGINT COMMENT '时间戳'
      )
  2. 基于Tunnel Upload 将本地数据文件内容导入MaxCompute的表中。更多Tunnel操作,请参见Tunnel命令

    Tunnel upload -fd | path_to_file/u.user pyodps_ml_100k_users;
    Tunnel upload -fd | path_to_file/u.item pyodps_ml_100k_movies;
    Tunnel upload -fd | path_to_file/u.data pyodps_ml_100k_ratings;

DataFrame对象操作

现在已经有了三张表,分别是pyodps_ml_100k_movies(电影相关的数据)、pyodps_ml_100k_users(用户相关的数据)、pyodps_ml_100k_ratings(评分有关的数据)。以下示例使用IPython运行。

说明

确保已经安装了Python。IPython是基于Python的,所以需要先安装Python环境。接着通过pip install IPython 安装IPython。安装完成后,可以通过执行IPython命令来启动IPython的交互式环境,开始编写和执行Python代码。

  1. 创建ODPS对象。

    import os
    from odps import ODPS
    # 确保 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量设置为用户 Access Key ID,
    # ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量设置为用户 Access Key Secret,
    # 不建议直接使用 Access Key ID / Access Key Secret 字符串
    o = ODPS(
        os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
        os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
        project='your-default-project',
        endpoint='your-end-point',
    )
    
  2. 通过传入Table对象创建一个DataFrame对象。

    from odps.df import DataFrame
    users = DataFrame(o.get_table('pyodps_ml_100k_users'))
  3. 您可以通过dtypes属性查看这个DataFrame的字段及字段类型。

    print(users.dtypes)

    返回值

    odps.Schema {
      user_id             int64
      age                 int64
      sex                 string
      occupation          string
      zip_code            string
    }
  4. 通过head方法,您可以取前N条数据并进行快速预览。

    print(users.head(10))

    返回值

       user_id  age  sex     occupation  zip_code
    0        1   24    M     technician     85711
    1        2   53    F          other     94043
    2        3   23    M         writer     32067
    3        4   24    M     technician     43537
    4        5   33    F          other     15213
    5        6   42    M      executive     98101
    6        7   57    M  administrator     91344
    7        8   36    M  administrator     05201
    8        9   29    M        student     01002
    9       10   53    M         lawyer     90703
  5. 如果您不需要看到所有字段,则可以进行如下操作:

    • 从中筛选出一部分字段。

      print(users[['user_id', 'age']].head(5))

      返回值

         user_id  age
      0        1   24
      1        2   53
      2        3   23
      3        4   24
      4        5   33
    • 只做排除个别字段操作。

      print(users.exclude('zip_code', 'age').head(5))

      返回值

         user_id  sex  occupation
      0        1    M  technician
      1        2    F       other
      2        3    M      writer
      3        4    M  technician
      4        5    F       other
    • 排除掉一些字段,通过计算得到一些新的列。例如将sexM的置为True,否则为False,并取名为sex_bool

      print(users.select(users.exclude('zip_code', 'sex'), sex_bool=users.sex == 'M').head(5))

      返回值

         user_id  age  occupation  sex_bool
      0        1   24  technician      True
      1        2   53       other     False
      2        3   23      writer      True
      3        4   24  technician      True
      4        5   33       other     False
  6. 查看男用户和女用户的个数。

    print(users.groupby(users.sex).agg(count=users.count()))

    返回值

       sex  count
    0    F    273
    1    M    670
  7. 将用户按职业划分,从高到低进行排序,查看人数最多的前10职业。

    df = users.groupby('occupation').agg(count=users['occupation'].count())
    print(df.sort(df['count'], ascending=False)[:10])

    返回值

          occupation  count
    0        student    196
    1          other    105
    2       educator     95
    3  administrator     79
    4       engineer     67
    5     programmer     66
    6      librarian     51
    7         writer     45
    8      executive     32
    9      scientist     31

    或者通过value_counts方法快速实现。该方法返回的行数受到options.df.odps.sort.limit的限制,详情请参见配置选项

    print(users.occupation.value_counts()[:10])

    返回值

          occupation  count
    0        student    196
    1          other    105
    2       educator     95
    3  administrator     79
    4       engineer     67
    5     programmer     66
    6      librarian     51
    7         writer     45
    8      executive     32
    9      scientist     31
  8. 通过更直观的图查看这份数据。在 IPython 中可以启用内嵌图表 。

    %matplotlib inline
  9. 将年龄分为30组,查看年龄分布的直方图。

    users.age.hist(bins=30, title="Distribution of users' ages", xlabel='age', ylabel='count of users')

    显示的图表为

    可视化图
  10. 此时,只需要使用join将这三张表联合起来,然后保存为一张新的表pyodps_ml_100k_lens

    movies = DataFrame(o.get_table('pyodps_ml_100k_movies'))
    ratings = DataFrame(o.get_table('pyodps_ml_100k_ratings'))
    
    o.delete_table('pyodps_ml_100k_lens', if_exists=True)
    lens = movies.join(ratings).join(users).persist('pyodps_ml_100k_lens')
    
    print(lens.dtypes)

    返回值

    odps.Schema {
      movie_id                            int64
      title                               string
      release_date                        string
      video_release_date                  string
      imdb_url                            string
      user_id                             int64
      rating                              int64
      unix_timestamp                      int64
      age                                 int64
      sex                                 string
      occupation                          string
      zip_code                            string
    }
  11. 将年龄(0到80岁)分成8个年龄段。

    labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79']
    cut_lens = lens[lens, lens.age.cut(range(0, 81, 10), right=False, labels=labels).rename('年龄分组')]
  12. 取分组和年龄唯一的前10条查看。

    print(cut_lens['年龄分组', 'age'].distinct()[:10])

    返回值

       年龄分组  age
    0       0-9    7
    1     10-19   10
    2     10-19   11
    3     10-19   13
    4     10-19   14
    5     10-19   15
    6     10-19   16
    7     10-19   17
    8     10-19   18
    9     10-19   19
  13. 最后,查看在各个年龄分组下,用户的评分总数和评分均值。

    print(cut_lens.groupby('年龄分组').agg(cut_lens.rating.count().rename('评分总数'), cut_lens.rating.mean().rename('评分均值')))

    返回值

         年龄分组  评分均值  评分总数
    0       0-9  3.767442        43
    1     10-19  3.486126      8181
    2     20-29  3.467333     39535
    3     30-39  3.554444     25696
    4     40-49  3.591772     15021
    5     50-59  3.635800      8704
    6     60-69  3.648875      2623
    7     70-79  3.649746       197

Dataframe数据处理

请您首先下载鸢尾花数据集。本文使用DataWorks PyODPS节点功能,详情请参见开发PyODPS 3任务

  1. 创建测试数据表 。

    使用DataWorks表管理功能新建表:

    1. 打开相应的业务流程,右键单击MaxCompute,选择新建表。在新建表对话框中,选择路径,输入名称,单击新建,进入表的编辑页面。

    2. 选择编辑页面左上角DDLimage.png

    3. 输入建表语句如下,完成后提交表。

      CREATE TABLE pyodps_iris (
          sepallength double COMMENT '片长度(cm)',
          sepalwidth double COMMENT '片宽度(cm)',
          petallength double COMMENT '瓣长度(cm)',
          petalwidth double COMMENT '瓣宽度(cm)',
          name string COMMENT '种类'
      ) ;
  2. 上传测试数据 。

    1. 在新建表上单击右键,选择导入数据,单击下一步,上传您刚下载的数据集。

      导入数据
    2. 单击按位置匹配后导入数据。

  3. 打开相应的业务流程,右键单击MaxCompute,选择新建节点,选择PyODPS 3,新建一个PyODPS节点,用于存放和运行代码。

  4. 输入代码后,单击运行image.png,运行后可在下方运行日志处查看结果。代码详情如下。

    from odps.df import DataFrame, output
    
    iris = DataFrame(o.get_table('pyodps_iris')) #从ODPS表创建DataFrame对象iris。
    print(iris.head(10))
    print(iris.sepallength.head(5))  #打印iris部分内容。
    
    # 使用自定义函数求iris的两列之和。
    print(iris.apply(lambda row: row.sepallength + row.sepalwidth, axis=1, reduce=True, types='float').rename('sepaladd').head(3))
    
    # 指定函数的输出名称和类型。
    @output(['iris_add', 'iris_sub'], ['float', 'float'])
    def handle(row):
        # 使用yield关键字可返回多行结果。
        yield row.sepallength - row.sepalwidth,row.sepallength + row.sepalwidth
        yield row.petallength - row.petalwidth,row.petallength + row.petalwidth
    
    # 打印前5行结果,axis=1表示列的轴沿着水平的方向。
    print(iris.apply(handle,axis=1).head(5))

    运行结果:

    # print(iris.head(10))
       sepallength  sepalwidth  petallength  petalwidth         name
    0          4.9         3.0          1.4         0.2  Iris-setosa
    1          4.7         3.2          1.3         0.2  Iris-setosa
    2          4.6         3.1          1.5         0.2  Iris-setosa
    3          5.0         3.6          1.4         0.2  Iris-setosa
    4          5.4         3.9          1.7         0.4  Iris-setosa
    5          4.6         3.4          1.4         0.3  Iris-setosa
    6          5.0         3.4          1.5         0.2  Iris-setosa
    7          4.4         2.9          1.4         0.2  Iris-setosa
    8          4.9         3.1          1.5         0.1  Iris-setosa
    9          5.4         3.7          1.5         0.2  Iris-setosa
    
    
    # print(iris.sepallength.head(5))
       sepallength
    0          4.9
    1          4.7
    2          4.6
    3          5.0
    4          5.4
    
    # print(iris.apply(lambda row: row.sepallength + row.sepalwidth, axis=1, reduce=True, types='float').rename('sepaladd').head(3))
       sepaladd
    0       7.9
    1       7.9
    2       7.7
    
    # print(iris.apply(handle,axis=1).head(5))
       iris_add  iris_sub
    0       1.9       7.9
    1       1.2       1.6
    2       1.5       7.9
    3       1.1       1.5
    4       1.5       7.7