内容简介:原文标题:A simple way to anonymize data with Python and Pandas原文链接 https://dev.to/r0f1/a-simple-way-to-anonymize-data-with-python-and-pandas-79g作者:Florian Rohrer
原文标题:A simple way to anonymize data with Python and Pandas
原文链接 https://dev.to/r0f1/a-simple-way-to-anonymize-data-with-python-and-pandas-79g
作者:Florian Rohrer
译者:大邓
最近,我收到了一个数据集,其中包含有关客户的敏感信息,而这些敏感信息是不能公开的。 数据集位于我们的一台服务器上,我认为这是一个相当安全的位置。 我想将数据复制到我的本地驱动,以便更舒适地处理数据,同时不必担心数据脱敏信息被泄露。 所以,我写了一个改变数据的小脚本用来将敏感数据进行脱敏华操作。 我将详细介绍我所采取的所有步骤,并重点介绍一些方便的技巧。
任务
任务是准备一个数据集,以便以后可以用于机器学习目的(例如分类,回归,聚类)而不包含任何敏感信息。 最终数据集不应与原始数据集有太大差异,应反映初始数据集的分布。我将使用Jupyter笔记本作为我的环境。
首先,让我们导入所有必要的库。
import pandas as pd import numpy as np import scipy.stats %matplotlib inline import matplotlib.pyplot as plt from sklearn_pandas import DataFrameMapper from sklearn.preprocessing import LabelEncoder # get rid of warnings import warnings warnings.filterwarnings("ignore") # 对每个jupyter cell能得到超过1个的输出 from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = "all" from utils import best_fit_distribution,plot_result
我将假设您已熟悉此处使用的大多数库。 我只想强调三件事:
-
sklearn_pandas
是一个方便的库,试图弥合两个包之间的差距。 它提供了一个DataFrameMapper类,它使得处理pandas.DataFrame更容易,它能让我们用更少的代码对数据进行重新编码。 -
来自IPython.core.interactiveshell…我更改了一个Jupyter Notebook默认值,这样就显示了多个输出。 关于其他方便的Jupyter技巧的一篇很好的博客文章就在这里。
-
最后,我们将把一些代码(best_fit_distribution,plot_result)放入一个名为utils.py的文件中,我们将放在Notebook旁边。
对于我们的分析,我们将使用 泰坦尼克数据集的titanic_train.csv
df = pd.read_csv("titanic_train.csv") df.shape df.head()
删除脱敏信息列
现在已经加载了数据,我们将删除所有可识别个人身份的信息。 列 [“PassengerId”,“Name”]
包含此类信息。
请注意, [“PassengerId”,“Name”]
对于每一行都是唯一的,因此如果我们构建机器学习模型,我们将在以后删除它们。
可以对 [“Ticket”,“Cabin”]
进行类似的论证,这几乎对于每一行都是唯一的。
在本文中为了方便,我们不会处理缺失值。 我们只是忽略所有包含缺失值的观察结果(如果行数据全为空,删除该行)。
df.drop(columns=["PassengerId", "Name"], inplace=True) df.drop(columns=["Ticket", "Cabin"], inplace=True) df.dropna(inplace=True) df.shape df.head()
类别型数据编码
随后,我们需要将 Sex
和 Embarked
这种类别型数据编码数字。
其中 Sex
编码为0和1, Embarked
编码为0,1,2。
LabelEncoder()
是类别型数据编码为数字的常用类。
encoders = [(["Sex"], LabelEncoder()), (["Embarked"], LabelEncoder())] mapper = DataFrameMapper(encoders, df_out=True) new_cols = mapper.fit_transform(df.copy()) df = pd.concat([df.drop(columns=["Sex", "Embarked"]), new_cols], axis="columns") df.shape df.head()
sklearn_pandas
库的 DataFrameMapper
类会接收元组组成的列表,其中元组的第一个元素是列名,元组的第二个元素是转换器(transformer)。类似的转换器还有MinMaxScaler()、StandardScaler()、FunctionTransformer()。
在最后一行代码中,我们将编码后的数据与剩下的数据合并起来。注意,axis参数可以传 axis=1
,也可以 axis='columns'
,但是推荐大家使用后一种方法,这样能增加代码的可读性。
#查看每个列名(字段)有几种值 df.nunique()
Survived 2 Pclass 3 Age 88 SibSp 6 Parch 7 Fare 219 Sex 2 Embarked 3 dtype: int64
离散型变量的处理
在上一面代码中我们打印了每个 列名(字段)
有几种值。一般这个变量的值的种类如果比较多,往往将该列(字段)定义为连续型变量;而当这个变量含有较少种类的值,往往将其定义为离散型变量。
在本文中,我们将阈值设置为20。当变量的值的种类大于20,定义为 连续型变量
。反之,定义为 离散型变量
import pandas as pd seires = pd.Series([1,2,1,1,1, 2]) seires.nunique() #结果是2
nunique()用来查看序列中有几种值
返回2,意思是series中只有两种值
categorical = [] #离散 continuous = [] #连续 #对列名进行迭代 for c in df.columns.tolist(): col = df[c] nunique = col.nunique() if nunique < 20: categorical.append(c) else: continuous.append(c) categorical
查看categorical内容,返回
['Survived', 'Pclass', 'SibSp', 'Parch', 'Sex', 'Embarked']
continuous
['Age', 'Fare']
numpy.random.choice(a, size=None, p=None)
首先我们决定每个字段(列名)作为变量,之后我们使用这个变量的概率函数,并将概率函数传入np.random.choice()来创建一个与原始数据分布类似的概率函数。在这里我们假设每个变量仅有5条记录。
for c in categorical: counts = df[c].value_counts() np.random.choice(list(counts.index), p=(counts/len(df)).values, size=5)
运行结果
array([1, 1, 0, 1, 1]) array([3, 3, 3, 1, 1]) array([0, 1, 0, 0, 0]) array([0, 0, 0, 0, 0]) array([0, 1, 0, 0, 1]) array([0, 1, 2, 2, 0])
连续型变量的处理
很幸运,连续型变量的处理方法已经在
stackoverflow thread找到,计算方法大致如下:
-
使用预先设定的多个数字间隔来创建一个hist直方图
-
使用一系列连续型分布函数,逐个运算匹配hist直方图。这个过程也会给分布函数生成参数
-
直到某个分布函数使得数据与hist拟合的最好,拥有最小的残差平方和。此时,该分布函数就是数据的分布函数。
具体代码可见utils.py文件。连续型变量只有两个变量,分别是Age和Fare
best_distributions = [] for c in continuous: data = df[c] best_fit_name, best_fit_params = best_fit_distribution(data, 50) best_distributions.append((best_fit_name, best_fit_params)) best_distributions
[('fisk', (11.744665309421649, -66.15529969956657, 94.73575225186589)), ('halfcauchy', (-5.537941926133496e-09, 17.86796415175786))]
Age变量是fisk分布,Fare变量是halfcauchy分布
best_distributions = [ ('fisk', (11.744665309421649, -66.15529969956657, 94.73575225186589)), ('halfcauchy', (-5.537941926133496e-09, 17.86796415175786))] plot_result(df, continuous, best_distributions)
现在我们将上面所有的过程封装成一个函数generate_like_df
def generate_like_df(df, categorical_cols, continuous_cols, best_distributions, n, seed=0): np.random.seed(seed) d = {} for c in categorical_cols: counts = df[c].value_counts() d[c] = np.random.choice(list(counts.index), p=(counts/len(df)).values, size=n) for c, bd in zip(continuous_cols, best_distributions): dist = getattr(scipy.stats, bd[0]) d[c] = dist.rvs(size=n, *bd[1]) return pd.DataFrame(d, columns=categorical_cols+continuous_cols)
gendf = generate_like_df(df, categorical, continuous, best_distributions, n=100) gendf.shape gendf.head()
往期文章
以上所述就是小编给大家介绍的《[译] 使用 sklearn 和 pandas 库对敏感数据进行匿名化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。