本質的に割り当ての問題があります。
私が持っているもの: 国勢調査区のような小さな地理的領域の観測があります。それぞれについて、4 つの異なる年齢層の人数を数えています。各トラクトはサブリージョンに属しています。
これで、小面積の分布が完全に正しいわけではないことがわかりました。なぜなら、より高いレベルの集計、サブリージョン レベル、およびより細かい地域レベルのデータを合計すると、異なるグループの合計が示されるという正しい分布がわかっているからです。
私が望むもの: 4つのグループにまたがるトラクトレベルの非集計分布を調整して、正しいことが知られている4つのグループにまたがるサマリーレベルの分布と一致するようにしたいが、トラクトレベルのシグナルを保持したい分布--つまり、より粗いデータに基づいて調整しますが、ウィンドウから放り出さないでください。
そこで、私がやりたいことは、次の基準を満たして、地域レベルの人口数を余白にシフトすることです。最初の 2 つは最も重要です (これらすべてを満たすことに関してはトレードオフがあることを認識しています)。
- 集計すると、サブリージョンの合計と一致する必要があります。
- 調整によって地域レベルの個体数が変化してはなりません。
- 既存の空間分布は実質的に変更されるべきではありませんが、新しい小地域の合計に従ってわずかに調整されただけです
- 理想的には、調整は公平であるべきです。つまり、調整は少数の記録だけでなく、各地域内により分散されるべきです。
以下は、モック データとプレースホルダー コードです。
まず、小領域データ:
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,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
1 つのアイデアは、各サブリージョン内の区画をサンプリングし、あるビンから別のビンに移動する必要がある人の総数にある程度比例して人を移動することですが、上記の基準を満たす賢い方法があるかどうかはわかりません.
私に問題を引き起こしているのは、主に、記録レベルの合計を維持し、信号として保持したい既存の空間分布を完全に破棄せずに、サブリージョンの合計に一致するようにグループ間で人々を再割り当てする方法を特定することです (しかし、現在知られている異なる全体的な分布に調整されています)。
大まかに言えば、詳細な分布をより集約的な分布に適合させる方法についてのアイデアはありgrp4 -> grp3
ますgrp2 -> 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)