一次更改一个字符串中的字母 (Pandas,Python3)

Change Letters in A String One at a Time (Pandas,Python3)

我在 Pandas (DF)

中有一个单词列表
Words
Shirt
Blouse
Sweater

我想做的是用我字典中的字母替换这些单词中的某些字母一次一个字母

例如:

mydict = {"e":"q,w",
          "a":"z"}

将创建一个新列表,首先一次替换列表中的所有 "e",然后再次迭代一次替换所有 "a":

Words
Shirt
Blouse
Sweater
Blousq
Blousw
Swqater
Swwater
Sweatqr
Sweatwr
Swezter

我一直在寻找解决方案:Mass string replace in python?

并尝试了以下代码,但它更改了所有实例 "e" 而不是一次一个地更改 - 有帮助吗?:

mydict = {"e":"q,w"}
s = DF
for k, v in mydict.items():
    for j in v:
          s['Words'] = s["Words"].str.replace(k, j)
DF["Words"] = s

这似乎也不起作用:

s = DF.replace({"Words": {"e": "q","w"}})

如果是循环,则需要在循环的每个周期更新s。您还需要遍历 v.

mydict = {"e":"q,w"}
s=deduped
for k, v in mydict.items():
     for j in v:
          s = s.replace(k, j)

然后将其重新分配给您的数据框:

df["Words"] = s

如果你可以将其写成一个接受一维数组(列表、numpy 数组等...)的函数,你可以使用 df.apply 将其应用于任何列,使用 df.apply().

因为您需要一次替换一个字母,这听起来不像是用 pandas 解决的好问题,因为 pandas 是一次完成所有事情(向量化操作) .我会将您的 DataFrame 转储到一个普通的旧列表中并使用列表操作:

words = DF.to_dict()["Words"].values()

for find, replace in reversed(sorted(mydict.items())):
    for word in words:
        occurences = word.count(find)
        if not occurences:
            print word
            continue
        start_index = 0
        for i in range(occurences):
            for replace_char in replace.split(","):
                modified_word = list(word)
                index = modified_word.index(find, start_index)
                modified_word[index] = replace_char
                modified_word = "".join(modified_word)
                print modified_word
            start_index = index + 1

给出:

Words
Shirt
Blousq
Blousw
Swqater
Swwater
Sweatqr
Sweatwr
Words
Shirt
Blouse
Swezter

您可以将它们附加到列表中,然后重新创建一个 DataFrame,而不是打印单词,如果这就是您想要的结果的话。

这个答案与 Brian 的 非常相似,但经过了一些净化并且输出没有重复:

words = ["Words", "Shirt", "Blouse", "Sweater"]
md = {"e": "q,w", "a": "z"}
md = {k: v.split(',') for k, v in md.items()}

newwords = []

for word in words:
    newwords.append(word)
    for c in md:
        occ = word.count(c)
        pos = 0
        for _ in range(occ):
            pos = word.find(c, pos)
            for r in md[c]:
                tmp = word[:pos] + r + word[pos+1:]
                newwords.append(tmp)
            pos += 1

newwords的内容:

['Words', 'Shirt', 'Blouse', 'Blousq', 'Blousw', 'Sweater', 'Swqater', 'Swwater', 'Sweatqr', 'Sweatwr', 'Swezter']

Prettyprint:

Words
Shirt
Blouse
Blousq
Blousw
Sweater
Swqater
Swwater
Sweatqr
Sweatwr
Swezter

任何错误都是当前时间的结果。 ;)


更新(解释)

tl;dr

The main idea is to find the occurences of the character in the word one after another. For each occurence we are then replacing it with the replacing-char (again one after another). The replaced word get's added to the output-list.

我将尝试逐步解释一切:

words = ["Words", "Shirt", "Blouse", "Sweater"]
md = {"e": "q,w", "a": "z"}

嗯。您的基本输入。 :)

md = {k: v.split(',') for k, v in md.items()}

一种更简单的处理替换字典的方法。 md 现在看起来像 {"e": ["q", "w"], "a": ["z"]}。现在我们不必以不同的方式处理 "q,w""z",但是替换的步骤是一样的,并且忽略了 "a" 只有一个替换字符这一事实。

newwords = []

用于存储输出的新列表。

for word in words:
    newwords.append(word)

我们必须对每个单词执行这些操作(我想,原因很清楚)。我们还将世界直接附加到我们刚刚创建的输出列表 (newwords)。

    for c in md:

c character 的缩写。因此,对于我们要替换的每个字符(md 的所有键),我们执行以下操作。

        occ = word.count(c)

occ for occurrences(是的。count 也适合 :P)。 word.count(c) returns character/string cword 中出现的次数。所以 "Sweater".count("o") => 0"Sweater".count("e") => 2。 我们在这里使用它来了解,我们必须多久查看一次 word 才能获得所有出现的 c.

        pos = 0

我们的起始位置是在word中寻找c。在下一个循环中使用。

        for _ in range(occ):

每次出现。由于连续数字在这里对我们没有价值,我们将其命名为 _ "discard"。此时 cword 中。然而

            pos = word.find(c, pos)

哦。看。我们找到了 c。 :) word.find(c, pos) returns cword 中第一次出现的索引,从 pos 开始。在开始时,这意味着从字符串的开头 => c 的第一次出现。但是通过这个调用我们已经更新了 pos。这加上最后一行 (pos += 1) 将我们的下一轮搜索 window 移动到刚好在上一次出现的 c.

之后开始
            for r in md[c]:

现在你明白了,我们之前更新 mc 的原因:我们现在可以轻松地对其进行迭代(旧 md 上的 md[c].split(',') 也可以完成这项工作)。所以我们现在正在为每个替换字符进行替换。

                tmp = word[:pos] + r + word[pos+1:]

实际替换。我们将其存储在 tmp 中(出于调试原因)。 word[:pos] 给我们 word 直到(当前)出现 c(不包括 c)。 r 是替代品。 word[pos+1:] 添加剩余的单词(同样没有 c)。

                newwords.append(tmp)

我们如此创建的新词 tmp 现在进入我们的输出列表 (newwords)。

            pos += 1

已经提到的pos调整为"jump over c"。


来自 OP 的附加问题: 是否有一种简单的方法来指示我要替换字符串中的多少个字母[(意思是例如一次多个)]?

当然可以。 但我目前对如何实现这一点只有一个模糊的想法。我要看看它,等我睡着了。 ;)

words = ["Words", "Shirt", "Blouse", "Sweater", "multipleeee"]
md = {"e": "q,w", "a": "z"}
md = {k: v.split(',') for k, v in md.items()}
num = 2     # this is the number of replaces at a time.

newwords = []

for word in words:
    newwords.append(word)
    for char in md:
        for r in md[char]:
            pos = multiples = 0
            current_word = word
            while current_word.find(char, pos) != -1:
                pos = current_word.find(char, pos)
                current_word = current_word[:pos] + r + current_word[pos+1:]
                pos += 1
                multiples += 1
                if multiples == num:
                    newwords.append(current_word)
                    multiples = 0
                    current_word = word

newwords的内容:

['Words', 'Shirt', 'Blouse', 'Sweater', 'Swqatqr', 'Swwatwr', 'multipleeee', 'multiplqqee', 'multipleeqq', 'multiplwwee', 'multipleeww']

Prettyprint:

Words
Shirt
Blouse
Sweater
Swqatqr
Swwatwr
multipleeee
multiplqqee
multipleeqq
multiplwwee
multipleeww

我添加了 multipleeee 来演示替换是如何工作的:对于 num = 2,这意味着前两次出现被替换,之后是接下来的两次。所以被替换的部分没有交集。如果您想要 ['multiplqqee', 'multipleqqe', 'multipleeqq'] 之类的东西,则必须存储 char 的 "first" 出现的位置。然后,您可以将 pos 恢复到 if multiples == num: 块中的那个位置。

如果您还有其他问题,请随时提问。 :)