统计聚合

使用统计聚合功能可以实现求最小值、求最大值、求和、求平均值、统计行数、去重统计行数、按字段值分组、按范围分组、按地理位置分组、按过滤条件分组等操作;同时多个统计聚合功能可以组合使用,满足复杂的查询需求。

说明

Python SDK 5.2.1及以上版本开始支持统计聚合功能。

背景信息

统计聚合的详细功能请参见下表。

功能

说明

最小值

返回一个字段中的最小值,类似于SQL中的min。

最大值

返回一个字段中的最大值,类似于SQL中的max。

返回数值字段的总数,类似于SQL中的sum。

平均值

返回数值字段的平均值,类似于SQL中的avg。

统计行数

返回指定字段值的数量或者多元索引数据总行数,类似于SQL中的count。

去重统计行数

返回指定字段不同值的数量,类似于SQL中的count(distinct)。

百分位统计

百分位统计常用来统计一组数据的百分位分布情况,例如在日常系统运维中统计每次请求访问的耗时情况时,需要关注系统请求耗时的P25、P50、P90、P99值等分布情况。

字段值分组

根据一个字段的值对查询结果进行分组,相同的字段值放到同一分组内,返回每个分组的值和该值对应的个数。

说明

当分组较大时,按字段值分组可能会存在误差。

范围分组

根据一个字段的范围对查询结果进行分组,字段值在某范围内放到同一分组内,返回每个范围中相应的item个数。

地理位置分组

根据距离某一个中心点的范围对查询结果进行分组,距离差值在某范围内放到同一分组内,返回每个范围中相应的item个数。

过滤条件分组

按照过滤条件对查询结果进行分组,获取每个过滤条件匹配到的数量,返回结果的顺序和添加过滤条件的顺序一致。

直方图统计

按照指定数据间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。

前提条件

  • 已初始化OTSClient。具体操作,请参见初始化
  • 已创建数据表并写入数据。
  • 已在数据表上创建多元索引。具体操作,请参见创建多元索引

最小值

返回一个字段中的最小值,类似于SQL中的min。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、DoubleDate类型。

    missing

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing值,则在统计聚合时会忽略该行。

    • 如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。

  • 示例

    统计年龄为18岁的人中得分的最低分数。

    query = TermQuery('age', 18)
    agg = Min('score', name = 'min')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=0, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

最大值

返回一个字段中的最大值,类似于SQL中的max。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、DoubleDate类型。

    missing

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing值,则在统计聚合时会忽略该行。

    • 如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。

  • 示例

    统计年龄为18岁的人中得分的最高分数。如果某人没有分数,则对应分数的默认值为0。

    query = TermQuery('age', 18)
    agg = Max('score', missing_value = 0, name = 'max')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=0, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

返回数值字段的总数,类似于SQL中的sum。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持LongDouble类型。

    missing

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing值,则在统计聚合时会忽略该行。

    • 如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。

  • 示例

    统计年龄为18岁的所有人得分的总和。

    query = TermQuery('age', 18)
    agg = Sum('score', name = 'sum')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

平均值

返回数值字段的平均值,类似于SQL中的avg。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、DoubleDate类型。

    missing

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing值,则在统计聚合时会忽略该行。

    • 如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。

  • 示例

    统计年龄为18岁的所有人得分的平均分。

    query = TermQuery('age', 18)
    agg = Avg('score', name = 'avg')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

统计行数

返回指定字段值的数量或者多元索引数据总行数,类似于SQL中的count。

说明

通过如下方式可以统计多元索引数据总行数或者某个query匹配的行数。

  • 使用统计聚合的count功能,在请求中设置count(*)。

  • 使用query功能的匹配行数,在query中设置setGetTotalCount(true);如果需要统计多元索引数据总行数,则使用MatchAllQuery。

如果需要获取多元索引数据某列出现的次数,则使用count(列名),可应用于稀疏列的场景。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、Double、Boolean、Keyword、Geo_pointDate类型。

  • 示例

    统计年龄为18岁的人中参加考试有分数的人数。

    
    query = TermQuery('age', 18)
    agg = Count('score', name = 'count')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

去重统计行数

返回指定字段不同值的数量,类似于SQL中的count(distinct)。

说明

去重统计行数的计算结果是个近似值。

  • 当去重统计行数小于1万时,计算结果接近精确值。

  • 当去重统计行数达到1亿时,计算结果的误差为2%左右。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、Double、Boolean、Keyword、Geo_pointDate类型。

    missing

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置Missing值,则在统计聚合时会忽略该行。

    • 如果设置了Missing值,则使用Missing值作为字段值的默认值参与统计聚合。

  • 示例

    去重统计年龄为18岁的人中一共有多少种不同的姓名。

    query = TermQuery('age', 18)
    agg = DistinctCount('name', name = 'distinct_name')
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('{\n"name":"%s",\n"value":%s\n}\n' % (agg_result.name, str(agg_result.value)))

百分位统计

百分位统计常用来统计一组数据的百分位分布情况,例如在日常系统运维中统计每次请求访问的耗时情况时,需要关注系统请求耗时的P25、P50、P90、P99值等分布情况。

说明

百分位统计为非精确统计,对不同百分位数值的计算精确度不同,较为极端的百分位数值更加准确,例如1%或99%的百分位数值会比50%的百分位数值准确。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、DoubleDate类型。

    percentiles

    百分位分布例如50、90、99,可根据需要设置一个或者多个百分位。

    missing_value

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing_value值,则在统计聚合时会忽略该行。

    • 如果设置了missing_value值,则使用missing_value值作为字段值的默认值参与统计聚合。

  • 示例

    query = TermQuery('product', '10010')
    agg = Percentiles('latency', percentiles_list = [50, 90, 95])
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, aggs=[agg]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for agg_result in search_response.agg_results:
        print('percentiles:%s' % agg_result.name)
        for item in agg_result.value:
            print('%s:%s' % (str(item.key), str(item.value)))

字段值分组

根据一个字段的值对查询结果进行分组,相同的字段值放到同一分组内,返回每个分组的值和该值对应的个数。

说明

当分组较大时,按字段值分组可能会存在误差。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Long、Double、Boolean、KeywordDate类型。

    size

    返回的分组数量,默认值为10。最大值为2000。当分组数量超过2000时,只会返回前2000个分组。

    group_by_sort

    分组中的item排序规则,默认按照分组中item的数量降序排序,多个排序则按照添加的顺序进行排列。支持的参数如下:

    • 按照值的字典序升序排列

    • 按照值的字典序降序排列

    • 按照行数升序排列

    • 按照行数降序排列

    • 按照子统计聚合结果中值升序排列

    • 按照子统计聚合结果中值降序排列

    sub_aggssub_group_bys

    子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。

    • 场景

      统计每个类别的商品数量,且统计每个类别价格的最大值和最小值。

    • 方法

      最外层的统计聚合是根据类别进行分组,再添加两个子统计聚合求价格的最大值和最小值。

    • 结果示例

      • 水果:5个(其中价格的最大值为15,最小值为3)

      • 洗漱用品:10个(其中价格的最大值为98,最小值为1)

      • 电子设备:3个(其中价格的最大值为8699,最小值为2300)

      • 其它:15个(其中价格的最大值为1000,最小值为80)

  • 示例1

    将年龄为18岁的人按分数分组,并获取人数最多的10个分数值以及每个分数的人数。

    query = TermQuery('age', 18)
    group_by = GroupByField('score', size = 10)
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=20, group_bys = [group_by]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("key:%s, count:%d" % (item.key, item.row_count))
  • 示例2

    将年龄为18岁的人按分数分组,并获取人数最少的2个分数值以及每个分数的人数。

    group_by = GroupByField('score', size = 2, group_by_sort = [RowCountSort(sort_order=SortOrder.ASC)])
    search_response = client.search(table_name, index_name,
                                    SearchQuery(TermQuery('age', 18), limit=100, get_total_count=True, group_bys = [group_by]),
                                    ColumnsToGet(return_type=ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("key:%s, count:%d" % (item.key, item.row_count))
  • 示例3

    将年龄为18岁的人按分数分组,并获取人数最多的2个分数值、每个分数的人数以及按主键排序前三的人的信息。

    sort = RowCountSort(sort_order = SortOrder.DESC)
    sub_agg = [TopRows(limit=3,sort=Sort([PrimaryKeySort(sort_order=SortOrder.DESC)]), name = 't1')]
    
    group_by = GroupByField('l', size = 2, group_by_sort = [sort], sub_aggs = sub_agg)
    search_response = client.search(table_name, index_name,
                                    SearchQuery(TermQuery('age', 18), limit=100, get_total_count=True, group_bys = [group_by]),
                                    ColumnsToGet(return_type=ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("\tkey:%s, count:%d" % (item.key, item.row_count))
            for sub_agg in item.sub_aggs:
                print("\t\tname:%s:" % sub_agg.name)
                for entry in sub_agg.value:
                    print("\t\t\tvalue:%s" % str(entry))
  • 示例4

    将年龄为18岁的人按分数和性别分组。

    sort = RowCountSort(sort_order = SortOrder.ASC)
    sub_group = GroupByField('sex', size = 10, group_by_sort = [sort])
    
    group_by = GroupByField('score', size = 10, group_by_sort = [sort], sub_group_bys = [sub_group])
    search_response = client.search(table_name, index_name,
                                    SearchQuery(TermQuery('age', 18), limit=100, get_total_count=True, group_bys = [group_by]),
                                    ColumnsToGet(return_type=ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("\tkey:%s, count:%d" % (item.key, item.row_count))
            for sub_group in item.sub_group_bys:
                print("\t\tname:%s:" % sub_group.name)
                for sub_item in sub_group.items:
                    print("\t\t\tkey:%s, count:%s" % (str(sub_item.key), str(sub_item.row_count)))

范围分组

根据一个字段的范围对查询结果进行分组,字段值在某范围内放到同一分组内,返回每个范围中相应的item个数。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持LongDouble类型。

    range[double_from, double_to)

    分组的范围。

    起始值double_from可以使用最小值Double.MIN_VALUE,结束值double_to可以使用最大值Double.MAX_VALUE。

    sub_aggssub_group_bys

    子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。

    例如按销量分组后再按省份分组,即可获得某个销量范围内哪个省比重比较大,实现方法是GroupByRange下添加一个GroupByField。

  • 示例

    统计年龄为18岁的人中得分的分数在[80, 90)[90, 100)两个区间段的人数。

    query = TermQuery('age', 18)
    group_by = GroupByRange(field_name = 'score', ranges = [(80, 90),(90, 100)])
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=0, group_bys = [group_by]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("range:%.1f~%.1f, count:%d" % (item.range_from, item.range_to, item.row_count))

地理位置分组

根据距离某一个中心点的范围对查询结果进行分组,距离差值在某范围内放到同一分组内,返回每个范围中相应的item个数。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持Geo_point类型。

    origin(double lat, double lon)

    起始中心点的经纬度。

    lat是起始中心点坐标纬度,lon是起始中心点坐标经度。

    range[double_from, double_to)

    分组的范围,单位为米。

    起始值double_from可以使用最小值Double.MIN_VALUE,结束值double_to可以使用最大值Double.MAX_VALUE。

    sub_aggssub_group_bys

    子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。

  • 示例

    统计年龄为18岁的人中家庭住址在距离学校一公里以内和一公里到两公里内的人数。其中学校经纬度为(31,116)。

    query = TermQuery('age', 18)
    group_by = GroupByGeoDistance(field_name = 'address', origin=GeoPoint(31, 116), ranges = [(0, 1000), (1000,2000)])
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, group_bys = [group_by]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("range:%.1f~%.1f, count:%d" % (item.range_from, item.range_to, item.row_count))

过滤条件分组

按照过滤条件对查询结果进行分组,获取每个过滤条件匹配到的数量,返回结果的顺序和添加过滤条件的顺序一致。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    filter

    过滤条件,返回结果的顺序和添加过滤条件的顺序一致。

    sub_aggssub_group_bys

    子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。

  • 示例

    分别统计年龄为18岁的人中数学考了100分和语文考了100分的人数。

    query = TermQuery('age', 18)
    filter1 = TermQuery('math', 100)
    filter2 = TermQuery('chinese', 100)
    filters = [filter1, filter2]
    group_by = GroupByFilter(filters)
    
    search_response = client.search(
        table_name, index_name,
        SearchQuery(query, next_token = None, limit=2, group_bys = [group_by]),
        columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        i = 0
        for item in group_by.items:
            print("filter:%s=%s, count:%d" % (str(filters[i].field_name), str(filters[i].column_value), item.row_count))
            i=i+1

直方图统计

按照指定数据间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。

  • 参数

    参数

    说明

    name

    自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。

    field

    用于统计聚合的字段,仅支持LongDouble类型。

    interval

    统计间隔。

    field_range[min,max]

    统计范围,与interval参数配合使用限制分组的数量。(fieldRange.max-fieldRange.min)/interval的值不能超过2000。

    min_doc_count

    最小行数。当分组中的行数小于最小行数时,不会返回此分组的统计结果。

    missing_value

    当某行数据中的字段为空时,字段值的默认值。

    • 如果未设置missing_value值,则在统计聚合时会忽略该行。

    • 如果设置了missing_value值,则使用missing_value值作为字段值的默认值参与统计聚合。

  • 示例

    query = TermQuery('product', '10010')
    group_by = GroupByHistogram(field_name = 'latency', interval = 100, field_range = FieldRange(0, 10000), missing_value = 0)
    
    search_response = client.search(table_name, index_name,
                                    SearchQuery(query, next_token = None, limit=2, group_bys = [group_by]),
                                    columns_to_get = ColumnsToGet(return_type = ColumnReturnType.ALL_FROM_INDEX))
    
    for group_by in search_response.group_by_results:
        print("name:%s" % group_by.name)
        print("groups:")
        for item in group_by.items:
            print("%s:%s" % (item.key, item.value))