首页 > 解决方案 > 通过在数据帧 Spark Scala 中将其替换为可发音的英文单词来匿名化 first_name、last_name 和 full_name 列

问题描述

我正在尝试用人类可读的替换来匿名化生产数据——这不仅会掩盖实际数据,还会给它一个可调用的身份以供识别。请帮助我了解如何在 Scala 中使用其他可发音的英文单词匿名化数据框列,如名字、姓氏、全名:

  1. 它必须将一个真实世界名称转换为另一个可发音和可识别的真实世界名称。
  2. 必须可以分别转换名字、姓氏和全名,使得全名=名字和姓氏之间用空格隔开。
  3. 它应该在每次迭代中为名称生成相同的匿名化名称。
  4. 目标数据集将有超过一百万条不同的记录。

我曾尝试遍历名词和形容词词典以达到两个可发音单词的组合,但它不会给我一百万个不同的组合。下面的代码:

def anonymizeString(s: Option[String]): Option[String] = {
  val AsciiUpperLetters = ('A' to 'Z').toList.filter(_.isLetter)
  val AsciiLowerLetters = ('a' to 'z').toList.filter(_.isLetter)
  val UtfLetters = (128.toChar to 256.toChar).toList.filter(_.isLetter)
  val Numbers = ('0' to '9')

  s match {
    //case None => None
    case _ =>
      val seed = scala.util.hashing.MurmurHash3.stringHash(s.get).abs
      val random = new scala.util.Random(seed)
      var r = ""
      for (c <- s.get) {
        if (Numbers.contains(c)) {
          r = r + (((random.nextInt.abs + c) % Numbers.size))
        } else if (AsciiUpperLetters.contains(c)) {
          r = r + AsciiUpperLetters(((random.nextInt.abs) % AsciiUpperLetters.size))
        } else if (AsciiLowerLetters.contains(c)) {
          r = r + AsciiLowerLetters(((random.nextInt.abs) % AsciiLowerLetters.size))
        } else if (UtfLetters.contains(c)) {
          r = r + UtfLetters(((random.nextInt.abs) % UtfLetters.size))
        } else {
          r = r + c
        }
      }
      Some(r)
  }

标签: scalaapache-spark-sqldata-maskinganonymize

解决方案


“它不会给我一百万种不同的组合”

我不知道你为什么这么说。我刚刚检查/usr/share/dict/words了我的 Mac,它有 234,371 个单词。这允许近 550亿个两个单词的组合。

因此,只需将您的字符串散列到 an Int,将其取模234,371,然后映射到字典中的相应条目。

诚然,字典中的某些单词看起来不太像名字(尽管仍然比你随机做的要好得多) - 例如“A”......但即使你要求单词包含至少 5 个字符,你还剩下 227,918 个单词——仍然绰绰有余。

不要在上面使用“裸体getOption……这太伤我的美感了:(

    class Anonymizer(dict: IndexedSeq[String]) {
       def anonymize(s: Option[String]) = s
         .map(_.hashCode % dict.size)
         .map(dict)
     }

推荐阅读