2

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

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

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

我想要什么: 我想调整我的区域级别,四组的分解分布,使其与已知正确的四个组的摘要级别分布一致,但保留区域级别的信号分布——即根据更粗略的数据对其进行调整,但不要将其扔出窗外。

那么,我想做的是将区域级人口计数转移到边缘,满足以下标准,前两个是最重要的(我意识到在满足所有这些方面需要权衡):

  1. 汇总后,它应与次区域总数相匹配。
  2. 调整不应改变道级人口。
  3. 现有的空间分布不应该有实质性的改变,而只是我根据新的次区域总数略微调整
  4. 理想情况下,调整应该是公平的——即调整不应该只是在少数记录上,而应该在每个区域内更加分散。

下面是模拟数据和占位符代码:

一、小面积数据:

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

第二,在次区域层面,小区域数据应该汇总到什么地方。

这些是次区域的“已知”分布。正是这些我希望调整区域级别的数据,以便在聚合区域时,它们大致匹配每个组中的这些区域总数。具体来说,总计grp4subregion A26,215,但根据目标,应该是22,000grp4 ,因此次区域 A 中的区域应该看到人员从其他一些组重新分类。

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

一个想法是在每个子区域内对区域进行采样,然后按照需要从一个垃圾箱转移到另一个垃圾箱的总人数按一定比例移动人员,尽管我不确定是否有一种聪明的方法可以满足上述标准.

引起我问题的主要是这确定了一种在各组之间重新分配人员以匹配次区域总数的方法,同时保持创纪录水平的总数,而不是完全抛弃预先存在的空间分布,我想将其保留为一个信号(但调整到现在已知的不同整体分布)。

一般而言,关于如何使详细分布适合更聚合的分布的任何想法,而不仅仅是采样区域和移动 x 人grp4 -> grp3grp2 -> grp1以及现有分布和目标分布之间的差异是什么?

占位符代码

这个函数主要是在一个表中查找每个组中的区域份额,将该分布推送到每个区域,所以它除了设置数据绑定之外什么都不做。

def some_redistribution_algorithm(df):
    """
    how many persons need to be moved across groups in each subregion?
    minimal solution is to just take those shifts and apply uniformly
    tracts keep the same counts, but the *distribution* across bins will change slightly
    Quality criteria for algorithm:
    - switch population at tract level such that 
    - tract-level population counts maintained
    - Pre- and post-adjustment spatial distribution be largely unchanged
    - change is not disproportional / dramatically impacting some tracts over others 
      (i.e. a tract with 10 grp4 population losing 8 would lose 80%, while a tract with 100 q4 hhs would lose 8%)

    """

    adjustments = summary_area_scale.xs(df.name)

    size = (adjustments).apply(lambda x: abs(x)).loc['grp4'].astype(np.int64)/df.shape[0]
    print "Processing %s (%s tracts), beg. pop: %s, avg pop to move (here q4) %s" %(df.name,df.shape[0],
                                                                                   df.sum().loc['grp4'].astype(np.int64),size)
    print 'Average pop per tract:'
    print df.sum()/df.shape[0]


    ## tract-level distribution, if all tracts had the same distribution within each subregion (placeholder)

    return df_small_area_scale.xs(df.name).mul(summary_area_scale_shares.unstack().xs(df.name),axis=1)

    #samplerows= np.random.choice(a=df.index, size=size)
    #df.loc[samplerows,:] = df.loc[samplerows,:]#, p=df.totalshare.tolist()),:]
df_small_area_scale.groupby(level=0).apply(some_redistribution_algorithm)
4

1 回答 1

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) 
于 2015-10-28T00:54:49.430 回答