import pandas as pd
import numpy as np
from typing import List, Dict, Tuple, Any
class KAnonymityGreedy:
"""
使用全序贪心算法实现K匿名化的类
K匿名化是指数据集中的每一条记录都与至少K-1条其他记录无法区分
全序贪心算法通过对数据排序,然后贪心合并相邻记录来实现K匿名
"""
def __init__(self, k: int = 3):
"""
初始化K匿名化处理器
参数:
k: 匿名化参数,每条记录至少需要与k-1条记录相似
"""
self.k = k
self.data = None
self.quasi_identifiers = [] # 准标识符列表
self.hierarchy = {} # 每个准标识符的泛化层次结构
self.sorted_data = None # 排序后的数据
def fit(self, data: pd.DataFrame, quasi_identifiers: List[str],
hierarchy: Dict[str, List[List[str]]]):
"""
拟合数据和参数
参数:
data: 待匿名化的数据集
quasi_identifiers: 准标识符列名列表
hierarchy: 每个准标识符的泛化层次结构
例如: {'age': [["1-10", "11-20", ...], ["1-20", "21-40", ...], ...]}
"""
self.data = data.copy()
self.quasi_identifiers = quasi_identifiers
self.hierarchy = hierarchy
# 检查准标识符是否都在数据中
for qi in quasi_identifiers:
if qi not in self.data.columns:
raise ValueError(f"准标识符 {qi} 不在数据列中")
if qi not in self.hierarchy:
raise ValueError(f"准标识符 {qi} 没有对应的泛化层次结构")
# 对数据进行排序(全序)
self.sorted_data = self.data.sort_values(by=quasi_identifiers).reset_index(drop=True)
return self
def _is_value_in_interval(self, value: Any, interval: str) -> bool:
"""
检查值是否在字符串表示的区间内
参数:
value: 要检查的值
interval: 字符串表示的区间,如"25-29"或"M"
返回:
布尔值,表示值是否在区间内
"""
# 处理单值区间(如性别)
if "-" not in interval:
return str(value) == interval
# 处理范围区间(如年龄、邮编)
try:
# 分割区间为上下限
lower_str, upper_str = interval.split("-")
lower = int(lower_str)
upper = int(upper_str)
# 检查值是否在区间内
return lower <= value <= upper
except (ValueError, TypeError):
return False
def _generalize_value(self, value: Any, qi: str, level: int) -> Any:
"""
对指定准标识符的特定值进行泛化
参数:
value: 要泛化的值
qi: 准标识符
level: 泛化层次
返回:
泛化后的值(字符串类型)
"""
# 获取该准标识符的泛化层次
levels = self.hierarchy[qi]
# 检查层次是否有效
if level < 0 or level >= len(levels):
return str(value) # 超出范围则返回字符串形式的原值
# 找到值所在的区间并返回区间表示
for interval in levels[level]:
if self._is_value_in_interval(value, interval):
return interval
# 如果未找到匹配的区间,返回原值的字符串形式
return str(value)
def _generalize_group(self, group: pd.DataFrame, levels: Dict[str, int]) -> pd.DataFrame:
"""
对一组数据按照指定的泛化层次进行泛化
参数:
group: 要泛化的数据组
levels: 每个准标识符的泛化层次
返回:
泛化后的数据组
"""
generalized = group.copy()
for qi in self.quasi_identifiers:
level = levels.get(qi, 0)
generalized[qi] = generalized[qi].apply(
lambda x: self._generalize_value(x, qi, level)
)
return generalized
def _calculate_cost(self, group: pd.DataFrame, levels: Dict[str, int]) -> float:
"""
计算泛化成本
参数:
group: 数据组
levels: 每个准标识符的泛化层次
返回:
泛化成本
"""
cost = 0.0
for qi in self.quasi_identifiers:
# 成本基于泛化层次和该组中不同值的数量
level = levels.get(qi, 0)
unique_values = group[qi].nunique()
# 泛化层次越高,成本越高;组内不同值越多,泛化成本越高
cost += level * unique_values
return cost
def anonymize(self) -> pd.DataFrame:
"""
执行K匿名化处理
返回:
匿名化后的数据集
"""
if self.sorted_data is None:
raise ValueError("请先调用fit方法拟合数据")
n = len(self.sorted_data)
if n < self.k:
raise ValueError(f"数据集大小({n})小于k值({self.k}),无法实现K匿名化")
anonymized = []
i = 0
# 全序贪心算法:从前往后处理,每次选择最优的k个记录组成一组
while i <= n - self.k:
# 尝试不同的组大小,寻找最优分组
best_cost = float('inf')
best_group_size = self.k
best_levels = {qi: 0 for qi in self.quasi_identifiers}
# 检查可能的组大小(从k到2k-1,或者直到数据结束)
max_group_size = min(2 * self.k - 1, n - i)
for group_size in range(self.k, max_group_size + 1):
group = self.sorted_data.iloc[i:i+group_size]
# 计算该组所需的泛化层次
levels = {}
for qi in self.quasi_identifiers:
# 找到最小的泛化层次,使得该组在该层次下可以匿名化
for level in range(len(self.hierarchy[qi])):
generalized = group[qi].apply(
lambda x: self._generalize_value(x, qi, level)
)
if generalized.nunique() == 1:
levels[qi] = level
break
else:
# 如果所有层次都无法使该组匿名化,则使用最高层次
levels[qi] = len(self.hierarchy[qi]) - 1
# 计算泛化成本
current_cost = self._calculate_cost(group, levels)
# 选择成本最低的分组
if current_cost < best_cost:
best_cost = current_cost
best_group_size = group_size
best_levels = levels
# 对最优分组进行泛化并添加到结果中
group = self.sorted_data.iloc[i:i+best_group_size]
generalized_group = self._generalize_group(group, best_levels)
anonymized.append(generalized_group)
# 移动到下一组
i += best_group_size
# 合并所有匿名化后的组
return pd.concat(anonymized, ignore_index=True)
# 示例用法
if __name__ == "__main__":
# 创建示例数据
data = pd.DataFrame({
'age': [25, 26, 27, 30, 31, 32, 45, 46, 47, 50, 51, 52],
'gender': ['M', 'M', 'F', 'F', 'M', 'F', 'M', 'F', 'F', 'M', 'F', 'M'],
'zipcode': [10001, 10002, 10003, 10004, 10005, 10006,
20001, 20002, 20003, 20004, 20005, 20006],
'disease': ['感冒', '发烧', '高血压', '糖尿病', '感冒', '发烧',
'高血压', '糖尿病', '感冒', '发烧', '高血压', '糖尿病']
})
# 定义准标识符
quasi_identifiers = ['age', 'gender', 'zipcode']
# 定义泛化层次结构(使用字符串表示区间)
# 年龄泛化层次: 0级(原始值), 1级(5岁区间), 2级(10岁区间), 3级(20岁区间)
age_hierarchy = [
["25", "26", "27", "30", "31", "32", "45", "46", "47", "50", "51", "52"],
["25-29", "30-34", "45-49", "50-54"],
["25-34", "45-54"],
["25-54"]
]
# 性别泛化层次: 0级(原始值), 1级(泛化为"人")
gender_hierarchy = [
["M", "F"],
["人"]
]
# 邮政编码泛化层次: 0级(原始值), 1级(前3位), 2级(前2位), 3级(前1位), 4级(任意)
zipcode_hierarchy = [
["10001", "10002", "10003", "10004", "10005", "10006",
"20001", "20002", "20003", "20004", "20005", "20006"],
["10000-10099", "20000-20099"],
["10000-19999", "20000-29999"],
["10000-99999"]
]
hierarchy = {
'age': age_hierarchy,
'gender': gender_hierarchy,
'zipcode': zipcode_hierarchy
}
# 创建K匿名化处理器并执行匿名化
kanon = KAnonymityGreedy(k=3)
kanon.fit(data, quasi_identifiers, hierarchy)
anonymized_data = kanon.anonymize()
print("原始数据:")
print(data[quasi_identifiers])
print("\nK匿名化后的数据 (k=3):")
print(anonymized_data[quasi_identifiers])
将上面python转换成Java代码