【问题标题】:Change Letters in A String One at a Time (Pandas,Python3)一次更改一个字符串中的字母(Pandas,Python3)
【发布时间】:2015-04-20 00:57:52
【问题描述】:

我有一个 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"}})

【问题讨论】:

  • 这是故意的吗,Shirt 出现两次,而输出中缺少Sweater
  • 很好,是一个错误。更新列表

标签: python-3.x pandas


【解决方案1】:

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

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']

漂亮的打印:

Words
Shirt
Blouse
Blousq
Blousw
Sweater
Swqater
Swwater
Sweatqr
Sweatwr
Swezter

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


更新(说明)

tl;dr

主要思想是逐个查找单词中字符的出现。对于每一次出现,我们都用替换字符替换它(一次又一次)。被替换的单词 get 被添加到输出列表中。

我会尝试一步一步解释一切:

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:

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

        occ = word.count(c)

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

        pos = 0

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

        for _ in range(occ):

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

            pos = word.find(c, pos)

哦。看。我们找到了c。 :) word.find(c, pos) 返回cword 中第一次出现的索引,从pos 开始。在开头,这意味着从字符串的开头 => 第一次出现c。但是通过这个电话,我们已经更新了pos。这加上最后一行 (pos += 1) 将我们的搜索窗口移动到下一轮的搜索窗口,使其刚好在上一次出现的 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 调整为“跳过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']

漂亮的打印:

Words
Shirt
Blouse
Sweater
Swqatqr
Swwatwr
multipleeee
multiplqqee
multipleeqq
multiplwwee
multipleeww

我添加了multipleeee 来演示替换是如何工作的:对于num = 2,这意味着前两个出现被替换,在它们之后,接下来的两个被替换。所以更换的零件没有交叉点。如果您想要['multiplqqee', 'multipleqqe', 'multipleeqq'] 之类的内容,则必须存储char 的“第一次”出现的位置。然后您可以将pos 恢复到if multiples == num:-block 中的那个位置。

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

【讨论】:

  • 嘿戴夫,解决方案有效!希望解释一下如何 :) 另外,是否有一种简单的方法来指示我要替换的字符串中有多少个字母(例如,而不是一次一个,一次两个等)
  • 这非常聪明——脱帽致敬!再次感谢您花时间解释。这对理解很有帮助:)
  • 我很高兴它有帮助:)
【解决方案2】:

因为您需要一次替换一个字母,这听起来不是一个用 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,而不是打印这些单词,如果这是您想要的结果。

【讨论】:

    【解决方案3】:

    如果您正在循环,则需要在循环的每个循环中更新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()

    【讨论】:

    • 嘿亚当 -- 感谢您的回复。代码只是返回原始列而没有替换,并出现以下错误:“SettingWithCopyWarning:试图在 DataFrame 的切片副本上设置值。尝试使用 .loc[row_index,col_indexer] = value 代替”
    • 我建议不要在循环中使用 s['words'] ,因为这将返回数据帧的整个列作为参考。如果你复制它会怎样:snew = copy.copy(s['words'])。它可能会挂起,因为您传递的是对实际列的引用,而不是创建一个新数组、对其进行变异并重新分配它。这有意义吗?
    猜你喜欢
    • 1970-01-01
    • 2017-06-08
    • 2016-05-09
    • 2023-03-16
    • 2021-05-28
    • 1970-01-01
    • 2010-11-16
    • 2016-12-25
    相关资源
    最近更新 更多