转移分解分布以匹配更多聚合级别分布

时间:2015-10-26 23:32:32

标签: python numpy pandas wiki frequency-distribution

我有什么本质上是分配问题。

我有什么: 我对人口普查区等小地理区域进行了观察。对于每个人,我有四个不同年龄组的人数。每个属于一个分区。

现在,我知道小区域分布并不完全正确,因为我知道正确的分布 - 在更高的聚合级别,子区域级别和更精细的级别数据,总结时,显示不同的组总数。

我想拥有什么: 我想调整我的管道级别,分解四个组的分布,以便与已知正确的四个组的汇总级别分布一致,但保留了道路级分布的信号 - 即。根据更粗糙的数据进行调整,但不要将其抛出窗口。

然后,我想做的是将管道级别的人口数量转移到边缘,符合以下标准,前两个是最重要的(我意识到在满足所有这些方面存在权衡) :

  1. 在汇总时,应匹配次区域总数。
  2. 调整不应改变道次水平。
  3. 现有的空间分布不应发生重大变化,只是根据新的次区域总数略微调整
  4. 理想情况下,调整应该是公平的 - 即。调整不应仅限于少数记录,而应更多地分布在每个区域内。
  5. 下面是模拟数据和占位符代码:

    首先,小区域数据:

    n=1000
    np.random.seed(123)
    df_small_area_scale = pd.DataFrame(data={
            'grp1':np.random.randint(10,250,n),
            'grp2':np.random.randint(10,250,n),
            'grp3':np.random.randint(10,250,n),
            'grp4':np.random.randint(10,250,n),
            'subregion': np.random.choice(['A', 'B', 'C', 'D', 'E'],n),
            'tract_id':range(1000)}).set_index(['subregion','tract_id'])
    
    
    df_small_area_scale.head()
                        grp1  grp2  grp3  grp4
    subregion tract_id                        
    B         0          119    85    11    19
    D         1          136   100    46   239
    A         2           76    26   198   109
    B         3          230   180    84   222
    A         4          108   101   222   244
    

    并且,通过subregion汇总这一点,我们得到了这个:

    df_small_area_scale.groupby(level=0).sum()
                grp1   grp2   grp3   grp4
    subregion                            
    A          27241  27050  27471  26215
    B          26507  24696  23315  24857
    C          27474  28871  28882  28743
    D          26671  26163  25077  27612
    E          22739  23077  23797  24473
    

    (并让我们获得每组中每个子区域的目标份额)

    summary_area_scale_shares = summary_area_scale.stack().groupby(level=0).apply(lambda x: x/float(x.sum()))
    summary_area_scale_shares.head()
    
    subregion      
    A          grp1    0.244444
               grp2    0.266667
               grp3    0.244444
               grp4    0.244444
    B          grp1    0.255319
    dtype: float64
    

    其次,小区域数据应该在次区域一级总和。

    这些是次区域"已知" 发行版。正是这些我想要调整管道级数据,这样当汇总时,它们大致匹配每组中的这些区域总数。具体来说,grp4中的subregion A总计为26,215,但根据目标,它应该 22,000 ,因此子区域A中的小册子应该看到人员从{{1}重新分类对其他一些团体。

    grp4

    一个想法是在每个子区域内对小册子进行抽样,然后将人们按一定比例移动到需要从一个箱子移动到另一个箱子的总人数,尽管我不确定是否有一种聪明的方式来进行会议以上标准。

    导致我出现问题的主要原因在于确定了一种在不同群体中重新分配人员以匹配次区域总数的方法,同时保持记录级别的总数而不是完全丢弃已有的空间分布,我希望将其保留为信号(但调整到现在已知的不同总体分布)。

    关于如何使一个细节分布更合理的任何想法,不仅仅是从summary_area_scale = pd.DataFrame(data={'grp1':[22000,24000,21000,25000,28000], 'grp2':[24000,22000,26000,20000,28000], 'grp3':[22000,24000,21000,25000,28000], 'grp4':[22000,24000,21000,25000,28000], 'subregion':list('ABCDE')}).set_index('subregion') summary_area_scale grp1 grp2 grp3 grp4 subregion A 22000 24000 22000 22000 B 24000 22000 24000 24000 C 21000 26000 21000 21000 D 25000 20000 25000 25000 E 28000 28000 28000 28000 grp4 -> grp3中抽取x个人以及两者之间的差异。现有和目标分布?

    占位符代码

    这个函数主要是在每个组中具有区域份额的表中查找,将该分布推送到每个区域,因此除了设置数据绑定之外它不做任何事情。

    grp2 -> grp1

1 个答案:

答案 0 :(得分:1)

如果我理解你的问题,我认为迭代比例拟合可能是你正在寻找的。如果可以的话,我会说出我最近遇到的类似问题。这是我试图解决的问题:

我知道大都市区的年龄分布,我知道每个地区的人口总数,但由于人口普查的运作方式,我想我知道每个地区的年龄分布,但我知道不确定。

我知道我想要满足管道内的总人口(行边缘),我知道我希望在大都市层面(柱边缘)满足年龄分布,我希望"种子" ipf与每个领域的分布,这是我对答案的最佳猜测。当然它不起作用(我的意思是数字不会加起来),所以你立即偏离这个猜测以满足边缘。这就是迭代比例拟合的目的。

也许不是防弹,但我使用的代码(在Python / numpy中)是这样的:

# this should be fairly self explanitory if you know ipf
# seed_matrix is your best bet at the totals, col_marginals are
# observed column marginals and row_marginals is the same for rows

def simple_ipf(seed_matrix, col_marginals, row_marginals, tolerance=1, cnt=0):
    assert np.absolute(row_marginals.sum() - col_marginals.sum()) < 5.0

    # first normalize on columns
    ratios = col_marginals / seed_matrix.sum(axis=0)
    seed_matrix *= ratios
    closeness = np.absolute(row_marginals - seed_matrix.sum(axis=1)).sum()
    assert np.absolute(col_marginals - seed_matrix.sum(axis=0)).sum() < .01
    # print "row closeness", closeness
    if closeness < tolerance:
        return seed_matrix

    # first normalize on rows
    ratios = row_marginals / seed_matrix.sum(axis=1)
    ratios[row_marginals == 0] = 0
    seed_matrix = seed_matrix * ratios.reshape((ratios.size, 1))
    assert np.absolute(row_marginals - seed_matrix.sum(axis=1)).sum() < .01
    closeness = np.absolute(col_marginals - seed_matrix.sum(axis=0)).sum()
    # print "col closeness", closeness
    if closeness < tolerance:
        return seed_matrix

    if cnt >= 50:
        return seed_matrix

    return simple_ipf(seed_matrix, col_marginals, row_marginals,
                      tolerance, cnt+1)