代码之家  ›  专栏  ›  技术社区  ›  dina

根据组最小值和最大值进行动态交叉连接

  •  1
  • dina  · 技术社区  · 5 年前

    这是我的数据

    data = [
     {'shape': 'circle', 'height': 5},
     {'shape': 'circle', 'height': 2},
     {'shape': 'square', 'height': 6}
    ]
    

    在上面的例子中
    对于“圆圈”,范围为2-5,
    对于“平方”范围为6,

    data = [
     {'shape': 'circle', 'height': 2},
     {'shape': 'circle', 'height': 3},
     {'shape': 'circle', 'height': 4},
     {'shape': 'circle', 'height': 5},
     {'shape': 'square', 'height': 6}
    ]
    

    有没有办法用熊猫来做这个,比如交叉连接, 不使用数据帧上的for循环
    这是我尝试过的代码,它有一个问题-(见结尾)

        from itertools import product
    
        df = pd.DataFrame(data)
        # get missing values
        min_height = df['height'].min()
        max_height = df['height'].max()
        all_heights = list(range(min_height, max_height + 1))
    
        # create full values df
        full_shape_list_df = pd.DataFrame(
            list(product(list(df['shape'].unique()), all_heights)),
            columns=['shape', 'height']
        )
    
        # merge with existing df
        df = pd.merge(
            df,
            full_shape_list_df,
            how='outer',
            on=['shape', 'height']
        ).drop_duplicates().sort_values(['shape', 'height'])
    

    此解决方案的问题是,范围为2-6 全部的 形状,结果是:

    [{'shape': 'circle', 'height': 2},
     {'shape': 'circle', 'height': 3},
     {'shape': 'circle', 'height': 4},
     {'shape': 'circle', 'height': 5},
     {'shape': 'circle', 'height': 6},
     {'shape': 'square', 'height': 2},
     {'shape': 'square', 'height': 3},
     {'shape': 'square', 'height': 4},
     {'shape': 'square', 'height': 5},
     {'shape': 'square', 'height': 6}]
    

    shape_height_min_max_df = df.groupby('shape').height.agg(['min', 'max'])
    # now do here some cross join (avoid for loops) - how?
    
    2 回复  |  直到 5 年前
        1
  •  2
  •   Henry Ecker Super Kai - Kazuya Ito    5 年前

    我们可以使用非常类似的方法,除了 groupby aggregate 变成 list 范围值从 min max 然后是每组的值 DataFrame.explode

    df = df.groupby('shape', as_index=False)['height'].agg(
        lambda x: np.arange(x.min(), x.max() + 1).tolist()
    ).explode('height', ignore_index=True)
    

    df :

        shape height
    0  circle      2
    1  circle      3
    2  circle      4
    3  circle      5
    4  square      6
    

    数据帧和导入:

    import numpy as np
    import pandas as pd
    
    df = pd.DataFrame({'shape': ['circle', 'circle', 'square'],
                       'height': [5, 2, 6]})
    

    我们还可以创建一个 MultiIndex.from_frame reindex 数据帧:

    midx = pd.MultiIndex.from_frame(
        df.groupby('shape', as_index=False)['height'].agg(
            lambda x: np.arange(x.min(), x.max() + 1).tolist()
        ).explode('height', ignore_index=True)
    )
    
    df = df.set_index(['shape', 'height']).reindex(midx, fill_value=0).reset_index()
    
        shape  height  width
    0  circle       2      3
    1  circle       3      0
    2  circle       4      0
    3  circle       5      4
    4  square       6      2
    

    数据帧和导入:

    import numpy as np
    import pandas as pd
    
    df = pd.DataFrame({'shape': ['circle', 'circle', 'square'],
                       'height': [5, 2, 6],
                       'width': [4, 3, 2]})
    

    说明:

    1. (+1,因为上限不包括在内):
    df.groupby('shape', as_index=False)['height'].agg(
        lambda x: np.arange(x.min(), x.max() + 1).tolist()
    )
    
        shape        height
    0  circle  [2, 3, 4, 5]
    1  square           [6]
    
    1. explode 将值列成行:
    df.groupby('shape', as_index=False)['height'].agg(
        lambda x: np.arange(x.min(), x.max() + 1).tolist()
    ).explode('height', ignore_index=True)
    
    0圈2
    1圈3
    2圈4
    3圈5
    4平方米6
    
        2
  •  1
  •   mozway    5 年前

    def reindex_fill(d):
        return (d.set_index('height')
                 .reindex(range(d['height'].min(),
                                d['height'].max()+1)
                         )
                 .ffill()
                 .reset_index()
               )
        
    df.groupby('shape', as_index=False).apply(reindex_fill).droplevel(0)
    

    输出:

       height   shape
    0       2  circle
    1       3  circle
    2       4  circle
    3       5  circle
    0       6  square
    

    height