【问题标题】:How can I replace Python Pandas table text values with unique IDs?如何用唯一 ID 替换 Python Pandas 表文本值?
【发布时间】:2016-12-21 02:43:12
【问题描述】:

我正在使用 Pandas 读取这种格式的文件:

fp = pandas.read_table("Measurements.txt")
fp.head()

"Aaron", 3, 5, 7  
"Aaron", 3, 6, 9  
"Aaron", 3, 6, 10 
"Brave", 4, 6, 0 
"Brave", 3, 6, 1

我想用唯一的 ID 替换每个名称,因此输出如下所示:

"1", 3, 5, 7 
"1", 3, 6, 9 
"1", 3, 6, 10 
"2", 4, 6, 0 
"2", 3, 6, 1

我该怎么做?

谢谢!

【问题讨论】:

    标签: python python-3.x pandas


    【解决方案1】:

    这可行,但需要注意的是:

    df = pd.DataFrame({"string_column": ["string1", "string2"]})
    df["hash"] = [hash(i) for i in df["string_column"]]
    df
    Out[1]: 
      string_column                 hash
    0       string1 -2164478207308662971
    1       string2 -3208847000100121065
    

    还有一点需要注意的是:哈希并不能 100% 保证是唯一的。两个不同的字符串可能具有相同的哈希值的可能性很小。但是,它是 20 位小数,因此很有可能是唯一的。

    最好的方法是确定列中的所有唯一值,为每个值分配一个递增的数字,然后遍历列中的值并替换为 ID。但是,这很慢,而且上面的行更快。

    【讨论】:

      【解决方案2】:

      我会使用categorical dtype:

      In [97]: x['ID'] = x.name.astype('category').cat.rename_categories(range(1, x.name.nunique()+1))
      
      In [98]: x
      Out[98]:
          name  v1  v2  v3 ID
      0  Aaron   3   5   7  1
      1  Aaron   3   6   9  1
      2  Aaron   3   6  10  1
      3  Brave   4   6   0  2
      4  Brave   3   6   1  2
      

      如果您需要字符串 ID 而不是数字 ID,您可以使用:

      x.name.astype('category').cat.rename_categories([str(x) for x in range(1,x.name.nunique()+1)])
      

      或者,正如@MedAli 在his answer 中提到的那样,使用factorize() 方法 - 演示:

      In [141]: x['cat'] = pd.Categorical((pd.factorize(x.name)[0] + 1).astype(str))
      
      In [142]: x
      Out[142]:
          name  v1  v2  v3 ID cat
      0  Aaron   3   5   7  1   1
      1  Aaron   3   6   9  1   1
      2  Aaron   3   6  10  1   1
      3  Brave   4   6   0  2   2
      4  Brave   3   6   1  2   2
      
      In [143]: x.dtypes
      Out[143]:
      name      object
      v1         int64
      v2         int64
      v3         int64
      ID      category
      cat     category
      dtype: object
      
      In [144]: x['cat'].cat.categories
      Out[144]: Index(['1', '2'], dtype='object')
      

      或将类别作为整数:

      In [154]: x['cat'] = pd.Categorical((pd.factorize(x.name)[0] + 1))
      
      In [155]: x
      Out[155]:
          name  v1  v2  v3 ID cat
      0  Aaron   3   5   7  1   1
      1  Aaron   3   6   9  1   1
      2  Aaron   3   6  10  1   1
      3  Brave   4   6   0  2   2
      4  Brave   3   6   1  2   2
      
      In [156]: x['cat'].cat.categories
      Out[156]: Int64Index([1, 2], dtype='int64')
      

      解释:

      In [99]: x.name.astype('category')
      Out[99]:
      0    Aaron
      1    Aaron
      2    Aaron
      3    Brave
      4    Brave
      Name: name, dtype: category
      Categories (2, object): [Aaron, Brave]
      
      In [100]: x.name.astype('category').cat.categories
      Out[100]: Index(['Aaron', 'Brave'], dtype='object')
      
      In [101]: x.name.astype('category').cat.rename_categories([1,2])
      Out[101]:
      0    1
      1    1
      2    1
      3    2
      4    2
      dtype: category
      Categories (2, int64): [1, 2]
      

      factorize() 方法的解释:

      In [157]: (pd.factorize(x.name)[0] + 1)
      Out[157]: array([1, 1, 1, 2, 2])
      
      In [158]: pd.Categorical((pd.factorize(x.name)[0] + 1))
      Out[158]:
      [1, 1, 1, 2, 2]
      Categories (2, int64): [1, 2]
      

      【讨论】:

      • 谢谢您 - 现在尝试您的建议!我已经编写了一个现在可以完成这项工作的函数,但是您的代码似乎是一个更优雅的解决方案。
      【解决方案3】:

      看起来Replace all occurrences of a string in a pandas dataframe 可能会保留您的答案。根据文档,pandas.read_table 创建了一个数据框,一个数据框有一个替换功能。

      fp.replace({'Aaron': '1'}, regex=True)
      

      虽然您可能不需要 regex=True 部分,因为它完全可以直接替换。

      【讨论】:

      • 谢谢 - 我一直在为更少的名字这样做,但在这种情况下,名字的数量是 >1000,所以一个接一个的替换不是一个选项。我已经编写了一个现在可以完成这项工作的函数,但是 pandas.factorize 似乎是一个更优雅的解决方案。
      【解决方案4】:

      您可以通过简单的字典映射来做到这一点。 例如,假设您的数据如下所示:

      col1, col2, col3, col4
      "Aaron", 3, 5, 7  
      "Aaron", 3, 6, 9  
      "Aaron", 3, 6, 10 
      "Brave", 4, 6, 0 
      "Brave", 3, 6, 1
      

      然后简单地做

      myDict = {"Aaron":"1", "Brave":"2"}
      fp["col1"] = fp["col1"].map(myDict)
      

      如果您不想构建字典,请使用pandas.factorize,它将为您从 0 开始编码列。您可以找到有关如何使用它的示例here

      【讨论】:

      • 谢谢!名字的数量很大,所以我现在正在尝试分解。我已经编写了一个现在可以完成这项工作的函数,但是 pandas.factorize 似乎是一个更优雅的解决方案。
      【解决方案5】:

      为什么不在名称上使用哈希

      df["col0"] = df["col0"].apply(lambda x: hashlib.sha256(x.encode("utf-8")).hexdigest())
      

      通过这种方式,您无需关心出现的名称,即您无需预先知道它们来构建映射字典。

      【讨论】:

      • 谢谢 - 我考虑过,但我使用的库需要简单的数值。
      猜你喜欢
      • 2018-06-23
      • 2022-01-24
      • 2021-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-31
      相关资源
      最近更新 更多