PWS Cup 2022

COVID-19 重篤化患者のプライバシーを守り切れるか？

ルール説明PWS Cup ワーキンググループ

グループ長 野島 良

1version
1.1

• 2022/09/22 更新情報
• ルール説明1.0→ルール説明1.1• 評価プログラムの公開および評価指標の記載を追記（Ｐ33～39）•

１．匿名化評価指標は、有用性評価２と、オッズ評価２（重篤化リスク評価）の４つになります。スコアはその平均値を表示しています。

•There are four anonymization evaluation indicators: usefulness rating 2 and odds rating. Scores are their averages.

•２．評価の年齢は年代でまとめてあります。

•The age of the usefulness evaluation is summarized by age.

•３．有用性評価相関行列については、本戦ではリターン値を変更します。

•For the usefulness evaluation correlation matrix, the return value will be changed in the Final Round.

•r e t u r n 1 - ( d f O D. c o r r ( ) - d f A D. c o r r ( ) ) . a b s ( ) . s u m ( ) . s u m ( ) / ( d f O D. s h a p e [ 1 ] * d f O D. s h a p e [ 1 ] * 2 )•※評価の年齢は有用性評価のみ年代でまとめてありますと報告しましたが、すべてに共通にしてあります。

2

PWS Cup 2022
匿名ヘルスケアデータコンテスト• COVID-19 重篤化患者のプライバシーを守り切れるか？
• コロナ重篤化患者の、性別、年齢、人種、学歴、病歴などからなるデータ• [*]において、NHANESをベースとした合成データが作成• 本合成データを参考• 誰のレコードかわからない様に匿名化しても、コロナで重篤化するリスクを算出できることを目指す[*]B. Seligman, M. Ferranna, D.E. Bloom, Social determinants of mortality from COVID-19: A simulation
3
study using NHANES, PLOS Medicine 18(12): e1003888

ストーリー• 登場人物• 加工者：コロナ重篤化患者からなるデータを匿名化する• 攻撃者：匿名化されたデータから、知人が重篤化したかどうかを特定する• 活用者：匿名化されたデータから、重篤化リスクを算出する• 審判（事務局）：どの加工者が正しく安全に加工しているか判定

4

データ
NHANES 概要•

National Health and Nutrition Examination
Sur vey•

CDC (米国疾病対策センター)の国民健康栄養調査プログラム•

1960年代から行われている調査．全米15箇所で，年5,000人を調査している．

•疫学研究，健全な公共健康政策やサービスの施策に活用•

被験者世帯は，NCHS所長からのレターを受け取る．報酬と診断結果を得る．プライバシーは法律で守られている ( p r i va c y i s p r o t e c t e d b y p u b l i c l a w s )5

スケジュール
5月• データセット整
備，ルール案6月• 有用性評価，• 安全性評価

7月

8月

9月

• トライアル• 予備戦 匿名化

• 予備戦 攻撃• 本戦 攻撃• 参加者募集開始

2022/8/18-

2022/9/2-

2022/10/7-

8/30

9/13

10/18

• 本戦 匿名化• リハーサル

2022/9/16-• 2022/10/26• ポスター，ウェ

2022/7/22‐

ブ

9/14

10/3

10月

CSSポスターセッション（終日）

6コンテスト用システム！
• 今年度は、コンテスト用のシステムを準備• みんなでオンラインで対戦しよう

7

参加方法とアクセス方法• 参加方法（2022/7/22～9/14）
PWSCUP2022ホームページ

https://www.iwsec.org /pws /2022/cup22.html

PWSCUP2022参加規程

https://www.iwsec.org /pws /2022/entry.html

をよくお読みになり、エントリーフォームから、お申し込みください。

• アクセス方法PWSCUP2022事務局から参加登録完了メールが届きます。
その後、大会システムのURLとIDとパスワード、利用規約をメールでうけとり、大会のシステムのマイページにアクセス可能となります。
本年度は締め切りました。

8コンテスト概要• 加工者：重篤化した患者のデータ（D）を匿名化（D ’）する
• 攻撃者：知人が重篤化したかどうかを知りたい

匿名化フェーズ

加工者

1

1

2

2

3

3

4

4…

…

D(orig_data): 加工前データ D’: 加工後データ
1
2
3
4…

攻撃フェーズ

攻撃者

D’: 加工後データr (ref_data) :知人のデータ入っている・入っていない
9

匿名化フェーズ
１．元データをダウンロード
２．事務局が提供する加工手法を使い、データを加工
３．結果をアップロード
４．システム上で自動採点
５．複数の結果の中から1つを選び提出

10

匿名化フェーズ詳細（加工方法）• 課題となるデータは６種類用意されており、マイページからダウンロードできます。
• ダウンロードしたデータは、事務局が提供するアルゴリズム（Python3）プログラムを使って各自の環境で加工し、加工手法のログとともにマイページにアップロードし、数秒後、採点結果を確認できます。
• データ加工した結果は複数アップロード可能（条件付き）ですが、最終結果としてその中から1つ選んで提出してください。
注）加工に利用するアルゴリズムはPWSCUPシステムのダウンロードサイトから入手が可能です
提出した匿名化データは攻撃フェーズでほかのチームからの攻撃対象データとして使われます。
11匿名化に使うデータ（加工前データ）
次のようなデータを匿名化してもらいます
コロナ重篤化した人のデータセット (300名)

年齢(Race): 20 〜 80
性別(Gender): 1 男性、２女性人種(Race) : 1 ヒスパニック系ではない白人
2 メキシコ系アメリカ人
3 その他のヒスパニック
4ヒスパニック系ではない黒人
6 ヒスパニック系ではないアジア人
7 その他
収入(Income): 1 〜15 大きいほど収入大77、99不明

病歴

教育(Education): 1 〜 5 大きいほど高学歴
5が大学卒業以上
7、９が不明軍歴（Veteran): 経験者は1経験者でなければ9所帯数（NOH）：最大 7
各病歴(症状があった場合１ なければ０)
HTN (高血圧）、DM （ 糖尿病）、IHD (心血管疾患)、CKD(慢性腎臓病)、COPD(慢性閉塞性肺疾患 )、CA （ガン）12

付録：データセット生成の概要• 本カップでは[* ]を参考にデータを生成しています。[*] では:• まず、 NHANES2 0 17 -2 0 1 8から各人（各レコード）が、年齢、人種、収入、教育レベル、軍歴、各種病歴からなるデータセットを抽出• 続けて、ガウスコピュラに基づきコロナ重篤化か否かを見積り、その擬似データを上記レコードに付与。
つまり、各人（各レコード）が、年齢、人種、収入、教育レベル、軍歴、各種病歴、コロナ重篤化 となる一部合成データとなるデータセット D0 を生成• 本カップで用いるデータセット• 加工前データ D：D0の内、コロナに重篤化した個人をランダムに30 0名選び加工前データ D とします。
• 攻撃用データ R：D0のうち、コロナに重篤化しなかった個人をランダムに2 5 名選び、さらにDから２5名をランダムに選び合算したデータ R# 本カップにおいては上記操作、つまりD 0を加工前データ毎に作っています。また、コロナに重篤化する個人の選び方は、原論文よりも高い確率としています。

[*] B. Seligman, M. Ferranna, D.E. Bloom, Social determinants of mortality fromCOVID-19: A simulation study using NHANES, PLOS Medicine 18(12): e1003888

13

top2.py
列col でしきい値 theta より大きい行を出力する．列は 1_5 の様に複数与えても良い．

for i in range(len(cols)):
ex &= (df.loc[:, int(cols[i])] < int(thetas[i]))コマンド: python top2.py 加工前.csv 加工後.csv col theta14bottom2.py
列col でしきい値 theta より小さい行を出力する．列は 1_5 の様にベクトルで与えても良い．

for i in range(len(cols)):
ex &= (df.loc[:, int(cols[i])] > int(thetas[i]))コマンド: python bottom2:py 加工前:csv 加工後:csv col theta15

kanony.py
列columnsを準識別子とみなしてk-匿名化する．削除する行を出力

def kanony(df, qi=[1, 2], k=1):
return df.groupby(qi).filter(lambda x: x[0].count() >= k)

コマンド: python kanony:py 加工前:csv 加工後:csv k col16

exclude.py
入力input .c svから排除行番号exclude を除いて出力する．

exclude_rows = list([int(i) for i in sys.argv[3].split("_")])
df = df.drop(index=df.index[exclude_rows])
コマンド
exclude.py 元ファイル名

4 結果ファイル名17

shuffle.py
行と列をランダムサンプリングする

df2 = df.sample(frac=1, random_state=int(sys.argv[3]))df2.to_csv(sys.argv[2], header=False, index=False)コマンド
suffle.py 元ファイル名

4 結果ファイル名18

rr.py列col において, 確率1 ‐ prob で他の値と置き換える. ただし,random は乱択アルゴリズムで利用する乱数とする.
def rr(x, q):u n i q = x . va l u e _ co u n t s () . in d e x . va l u e s
y = [ i i f ra n d o m . ra nd o m ( ) < q e l s e ra n d o m . c ho i c e ( uni q ) f o r i i n x ]r e t u rn( y )d e f r r d f ( d f, q , t a r g e t) :d f 2 = d f. c o p y ()f o r i i n t a r g e t:
d f 2 . i l o c [ :, i ] = r r ( d f. i lo c [ : , i ] , q )r e t u rn d f 2

コマンド: python rr:py 加工前:csv 加工後:csv prob col random19dp.py→lap.py

名前変更

差分プライバシーに基づいて行colsにεのラプラスノイズを付加する

def lap(x, eps):
x = (x + np.random.default_rng().laplace(0, 1/eps,x.shape[0]))return ((x * 2 + 1) // 2).astype(int)
コマンド: python dp.py 加工前.csv 加工後.csv col epsilon

四捨五入

20

top2_round.py
newfor i in range(len(cols)):
df.iloc[df.iloc[:,cols[i]] >= chop[i], cols[i]] = chop[i]df.to_csv(out, index = None, header = None)
コマンド: python top2_round.py 加工前:csv 加工後:csv coltheta
31
23
41

80

61

61以上を削除するのではなく、60にする
31

23

41

60

60

21

bottom2_round.py
newfor i in range(len(cols)):
df.iloc[df.iloc[:,cols[i]] <= chop[i], cols[i]] = chop[i]df.to_csv(out, index = None, header = None)python bottom2_round.py 加工前.csv 加工後.csv col theta
31234180

61

30未満を削除するのではなく、30にする
3130418061

22age_layer.pynew

年齢について…
312341806730

23408060

python age_layer.py 加工前.csv 加工後.csv
23

nn.pynewfor i in range(len(cols)):
df.iloc[rows[i],cols[i]] = 99
df.to_csv(out, index = None, header = None)

好きな場所を99（不明の意味）に置き換えることができる。
python nn.py 加工前.csv 加工後.csv 1_2 3_4 (1行3列目と2行4列目を99)
24average.py new
avg = df.iloc[rows,:].mean().astype(int)​ f or i in range(len(rows)):
df.iloc[rows[i]] = avg
df.to_csv(out, index = None, header = None)

指定した行の平均を取り、それらの行にその値を割り当てる
python nn.py 加工前.csv 加工後.csv 1_2_3_4
1, 2, 3, 4行目の平均を取り、それらを平均と置き換える25

average で意図していること
クラスタリング

Average で平均化する
exclude で削除する

クラスタリングして、平均に置き換える26

匿名化フェーズ詳細
提出物
加工データアップロード時に１．加工データ
２．加工に用いたアルゴリズムのログデータを提出します。
提出ファイルはアップロード時にフォーマットチェックが行われ、正しいデータがシステムに登録されます。
ログデータの例（正式なものは大会開始時に公開）：
python ../Anon/kanony2.py ../Data/orig_data1.csv anon_data1_k.csv 2 1_2
python ../Anon/rr.py anon_data1_k.csv anon_data2_rr.csv 0.2 1_2 31
python ../Anon/dp2.py anon_data2_rr.csv anon_data2_rr.csv 0 0.1 31
python ../Anon/top2.py anon_data2_rr.csv anon_data2_rr.csv 0 80
python ../Anon/bottom2.py anon_data2_rr.csv anon_data2_rr.csv 4 1
python ../Anon/shuffle.py anon_data2_rr.csv anon_data2_rr.csv 4python ../Anon/exclude.py anon_data2_rr.csv anon_data2_rr.csv 4

例：これを提出する加工データとしてください。
27

攻撃フェーズ①他のチームが作成した加工データ②事務局が用意した知人のデータを使って、知人が「加工後のデータ」に含まれているかどうかを推定します。
•知人のリストは2X名から構成されています。

•

X名がデータに含まれており、X名が含まれていない構成となっています。

•

的中させた人数をZ としたときに, 下記を得点としますmin ((2X – Z)/X, 1)•

二倍に攻撃回数は平等性を期すため制限があります。

１．攻撃対象データを選び、攻撃対象データと知人データをダウンロード
2．推定データを作成
３．結果をアップロード
４．システム上で自動採点

28

攻撃フェーズ詳細
提出物• 攻撃者は、他の参加者が加工した加工後データD’毎 に下記のような知人リスト R を得ます。
• Rの半分はD’ の加工前データDに含まれており, 残りの半分はDに含まれていません。
提出ファイル知人リストR
参加者（攻撃者）は、Rの各行について、Dに含まれていると判断すれば１、含まれていないと判断すれば0となるファイルを提出してください
29

総合評価
• 予備戦・本戦• 有用性評価: ３つの評価手法の平均。それぞれ最低０、最大１• 安全性評価：最大１、最低０ （破られていないものの割合）• 評価：有用性評価と安全性評価の平均• 総合評価• 予備戦 1 対 本戦 9 の比で評価
30

攻撃部門
• 攻撃部門：他チームの加工データに対して、どれだけメンバーシップ推定を成功させたか• 評価合算方法: 予備戦 1 対 本戦 9 の比で評価
Bチームの他チームに対する攻撃結果
A
0.2

BCDE

F

G

H

I

J

平均

0.3

0.2

0.1

1

0.1

0.2

0.1

0.2

0.26

攻撃していないところ評価点
31

二刀流部門• 二刀流部門: 総合部門と攻撃部門の結果の両方で一流だったチーム• 総合評価部門と攻撃部門の最終順位の平均で順位付け

32

有用性評価: 重篤化リスク評価• ロジスティック回帰分析：• COVID ~ AGE + GENDER + RACE + INCOME + EDUCATION+ VETERAN + NOH + HTN + DM + IHD + CKD + COPD + CA

• 加工データ、加工前データにおいてそれぞれロジスティック回帰分析を行う• それぞれの偏回帰係数（の指数乗）の差分の合計(OR比）• ただし、加工データには重篤化患者しか含まれないため、重篤化患者ではない個人のデータがプログラムに含まれている。 33

有用性評価: 重篤化リスク評価
def odds(df):model = smf.glm(formula='COVID ~AGE+GENDER+RACE+INCOME+EDUCATION+VETERAN+NOH+HTN+DM+IHD+CKD+COPD+CA', data=df, family=sm.families.Binomial() )res = model.fit()
df2 = pd.DataFrame(res.params, columns=['Coef'])
df2['OR'] = np.exp(res.params)
df2['pvalue'] = res.pvalues
return(df2)
def oddsDiff(df_anon, df_orig):
df_anon.columns = ['AGE','GENDER','RACE','INCOME','EDUCATION','VETERAN','NOH','HTN','DM','IHD','CKD','COPD','CA']
df_orig.columns = ['AGE','GENDER','RACE','INCOME','EDUCATION','VETERAN','NOH','HTN','DM','IHD','CKD','COPD','CA']
df_anon['COVID'] = 1
df_orig['COVID'] = 1
df_utility = pd.read_csv(os.path.join(os.path.dirname(__file__), "utility.csv") 配布済)
df_orig = pd.concat([df_orig, df_utility])
df_anon =pd.concat([df_anon, df_utility])
df_anon = odds(df_anon)['OR']
df_orig = odds(df_orig)['OR']
return([max(1-((df_anon - df_orig).abs().max())/20, 0), max(1-((df_anon - df_orig).abs().mean())/20, 0)])Score C

Score D34

有用性評価: 相関行列の差x

加工前データ

相関行列1

各要素の合算値 s

有用性評価値
1 - s/2*x2|相関行列1-相関行列2|

加工後データ

相関行列235

有用性評価: 相関行列の差
def corrDiff(dfOD, dfAD):#return 1 - (dfOD.corr()dfAD.corr()).abs().sum().sum()/(dfOD.shape[0]*dfOD.shape[1]*2)
return 1 - (dfOD.corr()dfAD.corr()).abs().sum().sum()/(dfOD.shape[1]*dfOD.shape[1]*2)
2022/09/22修正

Score B36

有用性評価: 集計数の差
年齢

人種

加工前データ

加工後データ

差分

20代

23

31

8

30代

10

11

1…

21

23

1080以上12

32

20

不明（99）

1231

19

1 ヒスパニック系ではない白人

32

23

9

4ヒスパニック系ではない黒人

31

23

8

6 ヒスパニック系ではないアジア人

31

33

27 その他

2212199 不明

1239044222211113299123737373737…

有用性評価値 1 – B/A…
CA

合計値A

合計値B37有用性評価: 集計数の差
def agDiff(dfOR, dfAD):
retOR = 0
retOR_RD =0for i in [0 , 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12]:
dfOR1=pd.DataFrame(dfOR.loc[:,i].value_counts().sort_index())
dfRD1=pd.DataFrame(dfAD.loc[:,i].value_counts().sort_index())
df_concat = pd.concat([dfOR1, dfRD1], axis=1).fillna(0)retOR2= abs(df_concat.iloc[:,0]).sum()
retOR_RD2 = abs(df_concat.iloc[:,0]-df_concat.iloc[:,1]).sum()
retOR=retOR+retOR2 #元データの集計数
retOR_RD=retOR_RD+retOR_RD2 #元データと匿名後の集計数の差分
return 1-retOR_RD/retOR

Score A

年代共通処理dfOD[0]=dfOD[0].apply(lambda x:math.floor(x/10)*10)
dfAD[0]=dfAD[0].apply(lambda x:math.floor(x/10)*10)

お願い• チームの代表者はCSS2022に参加登録を行い，最終日にプレゼンテーションをお願いします．
• ルール・システムなど、まだ検討中で変更するかもしれないことをご了承ください．
（留意事項） NHANESは倫理承認されており，CDCの趣旨に沿った分析には，追加の承認は不要であることをご承知おきく
ださい40