【问题标题】:Re-write loop to assign a number to every row in a column重写循环为列中的每一行分配一个数字
【发布时间】:2015-10-03 04:44:37
【问题描述】:

我有一个标题为CompanyNam 的列,我需要提取所有权信息。该列位于 QGIS 属性表中,但这并不重要。该列看起来像所附图片。

例如,如果CEZ 是第一家公司,那么我为其分配编号 1,然后Sokolovska 是下一个编号,等等。如果CEZ 再次出现在其他行中,它将获得编号 1 . 重要的是要注意,如果我在列中有NULL,我会为每个 NULL 行条目分配一个不同的数字。我需要与CompanyNam 输出相对应的数字。我有以下代码:

EmptyArray = []
d = {}
newlist = []
for gFeat in GeneratorLayer.getFeatures():
    Owner = gFeat.attributes()[gProvider.fieldNameIndex('CompanyNam')].toString()
    A = ([str(i) for i in Owner]) #convert from PyQt4.QtCore.QString to normal string
    B = ''.join(A)
    EmptyArray.append(B)
    for m, n in enumerate(EmptyArray):
        if n not in d:
            d[n] = [m+1]
        newlist.append({n: d[n]})
        if n == '':
            d[n] = [m+1]
        newlist.append({n: d[n]}) #Every NULL gets a new number
    for names in newlist:
        for o, p in names.iteritems():
            if o == '':
                a2 = str('{},NULL'.format(p))
            elif o != '':
                a2 = str('{},{}'.format(p,o))

然后我在进一步的步骤中使用a2。该代码对于 60-100 行的列运行良好,但对于较大的列,计算时间非常长。你能建议我用什么方法重写这段代码,保持逻辑吗? 输出如下所示:

[1],CEZ
[1],CEZ
[1],CEZ
[1],CEZ
[1],CEZ
[1],CEZ
[1],CEZ
[1],CEZ
[9],Sokolovska
[10],International
[11],ENERGOTRANS,
[12],Alpiq
[13],Mittal Steel
[14],United
[1],CEZ
[1],CEZ
[17],Dalkia.....

如果有编号[1 ], [2], [3] 而不是[1 ], [9], [10] 会更好,但我还没有弄清楚该怎么做。

【问题讨论】:

  • 如果有帮助,别忘了将答案标记为正确!

标签: python string loops dictionary


【解决方案1】:

我会使用一个列表,就像您附加项目一样,并使用一个单独的字典来获取数字:

EmptyArray = []
d = {}
newlist = []

for gFeat in GeneratorLayer.getFeatures():

    Owner = gFeat.attributes()[gProvider.fieldNameIndex('CompanyNam')].toString()

    A = ([str(i) for i in Owner]) #convert from PyQt4.QtCore.QString to normal string
    B = ''.join(A)

    EmptyArray.append(B)
    
    m = 1

    for n in EmptyArray:  # no need to enumerate as numbers should increment only on uniques

        if n not in d:  # this is a unique
            d[n] = [m]  # put number in dictionary, with key as val.
            m += 1  # so increment

        elif n == '':  # if it's blank (and if it is already in the dict)
            d[n].append(m)  # append new number, as blanks always increment
            m += 1  # increment

    for name in EmptyArray:  # looping less as only want to get names

        if name == '':  # if it's a blank, we want to pop out the first item in the list.
            a2 = str('{},NULL'.format(d[name].pop(0)))

        else:  # otherwise we just index the first item.
            a2 = str('{},{}'.format(d[name][0], name))

以上应该可以工作。

这样你就不必循环太多,逻辑更清晰一些。希望这有助于缩短计算时间,但也可以为您提供正确的数字。

在循环遍历 EmptyArray 时,我们总是附加到一个列表,这可能不是最好的方法,因为你只需要一个空白列表,并且不使用列表直接访问一个项目会更快 -但是当列表只有 1 项时,我怀疑会有很大的不同。

对于空格,我们需要使用一个列表,因此它可以接受多个数字(每个空格一个)。为了得到每个空格的正确数字,我们只需要在每次遇到空格时从列表前面弹出数字 - 这应该对应于最初分配给该空格的数字。

我们也不需要创建一个新列表,只需在 EmptyArray 上重复我们的循环即可。

为了提高效率

我们可以完全摆脱 EmptyArray 上的第二个循环(我没有看到任何额外的逻辑使它成为必要),只需对第一个循环执行以下操作:

    for n in EmptyArray:  # no need to enumerate as numbers should increment only on uniques

        if n == '' :  # blanks always increment - no need to store as we treat them as new
            a2 = str('{},NULL'.format(m))
            m += 1  # so increment

        elif n not in d:  # this is unique, so add to dict and increment
            d[n] = m  # add to dict for future reference
            a2 = str('{},{}'.format(m, n))
            m += 1  # increment

        else:  # it's in the dict, and not a blank, so grab from dict.
            a2 = str('{},{}'.format(d[n], n))

这样我们就摆脱了大量的循环,程序的效率应该会大大提高。它还消除了存储空白对应数字的需要,从而节省了额外的精力 - 我们可以使用对数字的直接引用来处理其他所有事情。

所以我做了两个函数来尝试复制你想要的:

EmptyArray = ['',1,2,3,'',1,'',3,2,'',1]
m = 1
a2 = ''

def f1 (EmptyArray, d, m, a2):
    for n in EmptyArray:  # no need to enumerate as numbers should increment only on uniques

        if n not in d:  # this is a unique
            d[n] = [m]  # put number in dictionary, with key as val.
            m += 1  # so increment

        elif n == '':  # if it's blank (and if it is already in the dict)
            d[n].append(m)  # append new number, as blanks always increment
            m += 1  # increment

    for name in EmptyArray:  # looping less as only want to get names

        if name == '':  # if it's a blank, we want to pop out the first item in the list.
            a2 += str('{},NULL\n'.format(d[name].pop(0)))

        else:  # otherwise we just index the first item.
            a2 += str('{},{}\n'.format(d[name][0], name))

    print(a2)

def f2 (EmptyArray, d, m, a2):
    for n in EmptyArray:  # no need to enumerate as numbers should increment only on uniques

        if n == '' :  # blanks always increment - no need to store as we treat them as new
            a2 += str('{},NULL\n'.format(m))
            m += 1  # so increment

        elif n not in d:  # this is unique, so add to dict and increment
            d[n] = m  # add to dict for future reference
            a2 += str('{},{}\n'.format(m, n))
            m += 1  # increment

        else:  # it's in the dict, and not a blank, so grab from dict.
            a2 += str('{},{}\n'.format(d[n], n))

    print(a2)

f1(EmptyArray, {}, m, a2)
f2(EmptyArray, {}, m, a2)

这里是调用的输出:

1,NULL
2,1
3,2
4,3
5,NULL
2,1
6,NULL
4,3
3,2
7,NULL
2,1

1,NULL
2,1
3,2
4,3
5,NULL
2,1
6,NULL
4,3
3,2
7,NULL
2,1

分别是f1和f2的时间:

3.4341892885662415e-05
1.5789376039385022e-05

所以 f2 比 f1 花费的时间少了一半。

【讨论】:

  • 感谢您的建议,我刚刚实现了它,但在此行中出现错误:a2 = str('{},NULL'.format(d[name].pop(0)) ) 它说:IndexError: pop from empty list
  • @EliDimitrova,尝试我在底部发布的解决方案,它替换了最后两个循环。它应该工作得最好。我会看看 IndexError 发生的原因。
  • 代码有效,我用n替换了最后一行的name,但是当我查看输出时,它给出:1,Sokolovska这是错误的,应该是2,Sokolovska。我第一次得到 2 时,第一个为 NULL
  • @EliDimitrova 不确定你为什么会出错,如果你看到上面的例子,它给了我完美的输出。它为 CEZ 提供了什么?
猜你喜欢
  • 2022-01-23
  • 1970-01-01
  • 2017-07-10
  • 1970-01-01
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
  • 1970-01-01
相关资源
最近更新 更多