0

我已经问过一个类似的问题(从字典中删除重复值而不删除键)但我认为我正在深入研究这个问题并且可以更好地解释它,因为不幸的是,没有一个答案符合我的需要,但他们确实回答了我的问题。

我正在从两个 .csv 文件构建一个 OrderedDict。第一个包含位置代码,而第二个是按时间排列的硬件重定位列表。所有位置代码都是唯一的,因此这是我的字典的关键,我有一个循环,使用该 .csv 文件构建具有空值的字典。然后我有另一个循环将硬件数据添加到正确的位置代码。一些硬件数据采用列表形式,因此不可散列。

我遇到的问题是,随着硬件移动到新位置,我需要将其从以前的位置移除。所以它只在代码末尾的一个地方

我的位置代码是;

>1, 2, 3, 4, 5, 6

我的硬件数据是按时间排序的,

>7pm, 1, 'item1', 'item2', 'item3'
>8pm, 2, 'item4', 'item5', 'item6'  
>9pm, 3, 'item7', '', ''
>10pm, 4, 'item8', '', ''
>11pm, 5, 'item1', 'item2', 'item3'
>12am, 6, 'item7', '', ''
>1am, 3, 'item4', 'item5', 'item6'

如果我在没有任何条件语句的情况下在整个时间范围内运行代码,我的最终字典看起来像

>myDict = {'1': ['item1', 'item2', 'item3'], '2': ['item4', 'item5', 'item6'],  
>'3': 'item7', '4': 'item8', '5': ['item1', 'item2', 'item3'], '6': 'item7'}

但我也需要它的样子;

>my Dict = {'1': '', '2':'', '3': ['item4', 'item5', 'item6'], '4':  
>'item8', '5': ['item1', 'item2', 'item3'], '6': 'item7'}

因为项目(值)没有以与添加位置(键)相同的顺序添加到字典中,所以在构建字典(添加值)时这样做很重要,因为我无法返回并且只是完成后删除重复项。

我尝试了很多事情并得到了不同的结果,但我最新的是

locationCSV =  open('location.csv', "r")
hardwareCSV =  open('hardware.csv', "r")
locationLines = locationCSV.readlines()
hardwareLines = hardwareCSV.readlines()
finalLoc = OrderedDict() 

for line in locationLines:
    locationList = line.split(",")
    code = locationList[0]
    finalLoc[code] = ""

for line in hardwareLines:
    hardwareList = line.split(",")
    hardwareData = [hardwareList[2],hardwareList[3],hardwareList[4]]
    for k, v in finalLoc.iteritems():
        if hardwareData in finalLoc.itervalues():
            finalLoc[k] = ""
    finalLoc[hardwareList[1]] = hardwareData

print finalLoc

这会将所有位置返回为空。我已经坚持了几天,所以任何帮助将不胜感激。

4

3 回答 3

0

我的快速而肮脏的版本,重点是维护订单字典。有一个 csv 模块负责读取和解析输入数据。

def removeItem(d, item):
    # remove item from d, if present
    for k,v in d.items():
        if item in v:
            v.remove(item)
            d[k] = v

d=OrderedDict()
for c in loc_codes: #['1','2',....]
    d[c]=[]
for line in hardware.split('\n'): # or read line from file
    if line:
        items = line.split(', ')
        items = [l.strip("'") for l in items]
        k = items[1].strip()
        v = items[2:]
        for item in v:
            removeItem(d,item)
            d[k] += [item]
print d

结果:

OrderedDict([('1', []), ('2', []), ('3', ['item7', 'item4', 'item5', 'item6']), ('4', ['item8']), ('5', ['item1', 'item2', 'item3']), ('6', ['item9'])])

我从[]值开始,因此很容易从列表中添加和删除项目。如果这很重要,您可以轻松地将[]值更改为。''对于大量数据,removeItem它不如使用集合或其他字典的效率高,但它是一种快速而明显的方式。此外,它不依赖于任何其他数据结构。

于 2013-08-27T21:19:04.697 回答
0

您的代码存在许多问题,甚至阻止您走得那么远,因此这不可能是您的真实代码。但是让我们来看看错误:


csvList = line.split(",")

这将为您提供诸如" 1"and之类的值" 'item1'",我无法想象这是您真正想要的。

事实上,您的行末尾有杂散空格这一事实意味着它们甚至不会匹配。例如,第二行的最后一个字符串是" 'item6' ",但最后一行是" 'item6'",它们不是同一个字符串。

csv如果您使用库而不是尝试自己做,这会容易得多。如果您只想快速解决问题,您可以strip输入每个条目:

csvList = [item.strip() for item in line.split(",")]

hardwareData = [csvList[2],csvList[3],csvList[4]]

由于您的某些行只有 3 列,因此这将引发IndexError. 如果您只想获得少于 3 个短行的值而不是提升,您可以执行以下操作:

hardwareData = csvList[2:5]

for k, v in finalLoc.iteritems():
    if hardwareData in finalLoc.itervalues():

对于每一行,您将浏览整个字典,对于每个条目,搜索整个字典以查看是否finalLoc是任何地方的值。因此,如果字典中已经有 10 个项目,您将找到已经存在 100 次的每一行。这意味着,如果您将找到的内容空白,对于每一行,您将每行空白 10 次。

你可能想要if hardwareData == v这里。


        finalLoc[key] = ""

您尚未key在向我们展示的代码中定义任何地方。如果您之前在某处定义它,它将为每一行清除 100 次中的相同值。否则,这只会​​引发NameError.

你可能想要finalLoc[k]这里。

如果你保留一个逆字典,将每个值映射到它的键,这整个部分会更简单(也更有效)。


无论如何,将所有这些修复程序放在一起,您的代码就可以工作:

from collections import OrderedDict

hardwareLines = """7pm, 1, 'item1', 'item2', 'item3'
8pm, 2, 'item4', 'item5', 'item6'  
9pm, 3, 'item7'
10pm, 4, 'item8'
11pm, 5, 'item1', 'item2', 'item3'
12am, 6, 'item9'
1am, 3, 'item4', 'item5', 'item6'""".splitlines()

finalLoc = OrderedDict() 

for line in hardwareLines: ##hardware is the second .csv
    csvList = [item.strip() for item in line.split(",")]
    hardwareData = csvList[2:5]
    for k, v in finalLoc.iteritems():
        if hardwareData == v:
            finalLoc[k] = ""
    finalLoc[csvList[1]] = hardwareData

for k, v in finalLoc.iteritems():
    print('{}: {}'.format(k, v))

输出是:

1: 
2: 
3: ["'item4'", "'item5'", "'item6'"]
4: ["'item8'"]
5: ["'item1'", "'item2'", "'item3'"]
6: ["'item9'"]

这是使用csv模块和逆映射的版本:

from collections import OrderedDict
import csv

hardwareLines = """7pm, 1, 'item1', 'item2', 'item3'
8pm, 2, 'item4', 'item5', 'item6'  
9pm, 3, 'item7'
10pm, 4, 'item8'
11pm, 5, 'item1', 'item2', 'item3'
12am, 6, 'item9'
1am, 3, 'item4', 'item5', 'item6'""".splitlines()

finalLoc = OrderedDict()

invmap = {}

for row in csv.reader(map(str.rstrip, hardwareLines), 
                      skipinitialspace=True, quotechar="'"):
    hardwareData = tuple(row[2:5])
    if hardwareData in invmap:
        finalLoc[invmap[hardwareData]] = ""
    finalLoc[row[1]] = list(hardwareData)
    invmap[hardwareData] = row[1]

for k, v in finalLoc.iteritems():
    print('{}: {}'.format(k, v))

我仍然需要显式地去除每一行多余的尾随空格,但除此之外,csv我为我处理了所有事情——并注意它还删除了每个值周围多余的引号。

同时,我不必弄清楚如何遍历项目并找到与当前值匹配的每个键,invmap而是让我一步一步查找它。(请注意,映射必须是 1 对 1,因为如果您已经两次遇到该值,则第一个已被删除。)


当然,即使修复了剥离和引用问题,结果仍然不是您想要的。您想要的输出显然将单元素列表展开为元素。如果你想这样做,你需要明确地这样做。但你可能希望这样。事实上,您可能希望使用[]而不是''作为您的“空”值。这样,您就知道该值始终是 0 个或多个项目的列表,而不必将空字符串视为 0 个值,将任何其他字符串视为 1 个值,将列表视为多个值。因此,当您处理它时,而不是编写这样的代码:

if value == '':
    return ''
elif isinstance(value, str):
    return process_one_value(value)
else:
    return map(process_one_value, value)

......你可以这样做:

return map(process_one_value, value)
于 2013-08-27T20:44:12.563 回答
-1

我不会担心这个的解析方面。因此,假设您已将数据加载为可用的格式,例如:

locations = [1, 2, 3, 4, 5, 6]
hardware = [
    ('7pm',  1, ['item1', 'item2', 'item3']),
    ('8pm',  2, ['item4', 'item5', 'item6']),
    ('9pm',  3, ['item7']),
    ('10pm', 4, ['item8']),
    ('11pm', 5, ['item1', 'item2', 'item3']),
    ('12am', 6, ['item9']),
    ('1am',  3, ['item4', 'item5', 'item6'])
]

(我强烈建议您将解析代码与数据处理代码分开。如果您的算法不与 CSV 解析代码混合,那么通过您的算法进行推理会容易得多。)

解决这个问题的关键是在处理数据时维护两个字典,一个将位置映射到这些位置的项目列表,另一个将项目映射到它们的位置。第二张地图是第一张地图的倒数。

拥有这些逆映射将使我们能够从任一方向查找信息。如果我们有一个位置,我们可以看到那里有什么物品,如果我们有一个物品,我们可以得到它的位置。

items_by_location = dict()   # The items in each location.
locations_by_item = dict()   # The location of each item.

# Start with an empty set for the list of items in each location.
for location in locations:
    items_by_location[location] = set()

# Iterate over each item in each hardware line one by one.
for time, location, items in hardware:
    for item in items:
        old_location = locations_by_item.get(item)
        new_location = location

        # Remove the item from its old location.
        if old_location:
            items_by_location[old_location].remove(item)

        # Add it to its new location.
        items_by_location[new_location].add(item)
        locations_by_item[item] = new_location

# Now we can iterate over the list and see where each item ended up.    
for location, items in items_by_location.items():
    print location, items

输出:

1 set([])
2 set([])
3 set(['item6', 'item7', 'item4', 'item5'])
4 set(['item8'])
5 set(['item2', 'item3', 'item1'])
6 set(['item9'])
于 2013-08-27T20:31:42.873 回答