编码分类变量,使得字符的存在和位置在文字字符串中都很重要

Encoding categorical variables such that both the presence as well as the position of characters matter in literal strings

假设我们有一个数据框,其最后一列由如下文字字符串组成:

df = pd.DataFrame(
        {
            "col1": ["C", "A", "B"],
            "col2": [4, 1.7, 1],
            "col3": ["SHRTYPPS", "PGYTCCCKAR", "VPCCYCCARE"],
        }
    )

请注意,1) 字符在字符串中的存在和 2) 它在字符串中的位置都很重要。

单热编码最后一列如下:

col3_lst = [list(i) for i in df.col3]
ids, U = pd.factorize(np.concatenate(col3_lst))
df_new = pd.DataFrame([np.isin(U, i) for i in col3_lst], columns=U).astype(int)
pd.concat([df, df_new], axis=1).drop(["col3"], axis=1)

这将导致:

  col1  col2  S  H  R  T  Y  P  G  C  K  A  V  E
0    C   4.0  1  1  1  1  1  1  0  0  0  0  0  0
1    A   1.7  0  0  1  1  1  1  1  1  1  1  0  0
2    B   1.0  0  0  1  0  1  1  0  1  0  1  1  1

但是,如您所见,订单并未得到相应处理。无论如何将有关字符在相应字符串中的位置的信息注入到输出数据帧中?例如,如果最后一个字符串中有四个 C,我们需要捕获该字母明显出现在第 3、4、6 和 7 位的事实信息。我正在寻找类似以下内容的内容:

  col1  col2     position_1    posistion_2    position_3    position_4     position_5  ....  
0    C   4.0         19             8              18           20            25       ....
1    A   1.7         16             7              25           20            3        ....
2    B   1.0         22             16             3            3             25       ....

,其中编码列的每个数字标签,$position_{i}$,属于英文字母表中后续字符的位置;即 A 为 1,B 为 2,等等...

或者更好的是,类似于以下内容:

  col1  col2     position_1_A   position_1_B  ...  posistion_2_A   posistion_2_B  ...  position_3_A   position_3_B  ...  position_4_A   position_4_B ...
0    C   4.0           0             0        ...        0               0        ...       0               0       ...            0            0    ...
1    A   1.7           0             0        ...        0               0        ...       0               0       ...            0            0    ...
2    B   1.0           0             0        ...        0               0        ...       0               0       ...            0            0    ...

谢谢,

好的,像这样应该可以解决问题:

result = df["col3"].str.upper()\
    .str.extractall("(.)")\
    .unstack().droplevel(0, axis=1)\
    .add_prefix('position_')
result.applymap(lambda x: ord(x) - 64 if pd.notna(x) else x)

在第一步中我们提取所有字符(我使用 extractall("(.)") 而不是 split("") 来不处理额外的字符 (\n)。

在第二个中,我们将字母映射到数字。

结果看起来像这样:

match   position_0  position_1  position_2  position_3  position_4  position_5  position_6  position_7  position_8  position_9
0       19          8           18          20          25          16          16          19          NaN         NaN
1       16          7           25          20          3           3           3           11          1           18.0
2       22          16          3           3           25          3           3           1           18          5.0

编辑:如果你想做一个 hot_encoding 使用 pd.get_dummies()

result = df["col3"].str.upper()\
    .str.extractall("(.)")\
    .unstack().droplevel(0, axis=1)\
    .add_prefix('position_')
pd.get_dummies(result)

哪个可以给你:

    position_0_P    position_0_S    position_0_V    ... position_9_R
0   0               1               0               ... 0
1   1               0               0               ... 1
2   0               0               1               ... 0

编辑 2:

如果您已经将缺失编码为 .,并且您希望使用序数编码将它们编码为缺失,则必须将 . 替换为 np.nan:

result = df["col3"].str.upper()\
    .str.extractall("(.)")\
    .unstack().droplevel(0, axis=1)\
    .add_prefix('position_')\
    .replace('.',np.nan)

其他一切保持不变。

例如:

df = pd.DataFrame(
        {
            "col1": ["C", "A", "B", "D"],
            "col2": [4, 1.7, 1, 12],
            "col3": ["SHRTYPPS", "PGYTCCCKAR", "VPCCYCCARE", "HY.RT..CCTCC"],
        }
    )

result = df["col3"].str.upper().str.extractall("(.)").unstack().droplevel(0, axis=1).add_prefix('position_').replace('.',np.nan)
result.applymap(lambda x: ord(x) - 64 if pd.notna(x) else x)

#match  position_0  position_1  position_2  ... position_11
#0      19          8           18.0        ... NaN
#1      16          7           25.0        ... NaN
#2      22          16          3.0         ... NaN
#3      8           25          NaN         ... 3.0

pd.get_dummies(result)

#   position_0_H    position_0_P    position_0_S    position_0_V    ... position_10_C   position_11_C
#0  0               0               1               0               ... 0               0
#1  0               1               0               0               ... 0               0
#2  0               0               0               1               ... 0               0
#3  1               0               0               0               ... 1               1

编辑 3:

如果您想将某些组(例如括号中的组)视为一个字符,您应该修改正则表达式以将其捕获为一个组。在这种情况下,如上所示的序号编码将不起作用(因为它占据了字母表中单个字母的位置)。但是 one-hot 编码应该可以正常工作。例如:

df = pd.DataFrame(
        {
            "col1": ["C", "D"],
            "col2": [4, 12],
            "col3": ["SHRTYPPS(hr3)", "HY.RT..CCTCC(hr4)"],
        }
    )

result = df["col3"].str.extractall("(\(.*\)|.)")\
            .unstack().droplevel(0, axis=1)\
            .add_prefix('position_')\
            .replace('.',np.nan)\
            .apply(lambda x: x.str.replace(r'[\(\)]+', '', regex = True))

pd.get_dummies(result)

#   position_0_H    position_0_S    ... position_11_C   position_12_hr4
#0  0               1               ... 0               0
#1  1               0               ... 1               1

最后一个应用只是为了去掉列名中的括号,它可以被删除,如果名称 position_12_(hr4)position_12_hr4 更好。