Python 从列中的类似值创建嵌套字典,并使用value作为键,因为字典包含具有该值的所有行

Python 从列中的类似值创建嵌套字典,并使用value作为键,因为字典包含具有该值的所有行,python,pandas,dictionary,Python,Pandas,Dictionary,全部,, 我有一个图像中边界框的数据框,每个框都在一个单独的行中。我要做的是为一个特定的图像合并所有的行 image xmin ymin xmax ymax label 0 bookstore_video0_40.jpg 763 899 806 940 pedestrian 3 bookstore_video0_40.jpg 1026 754 1075 797 pedestrian 4 bookst

全部,, 我有一个图像中边界框的数据框,每个框都在一个单独的行中。我要做的是为一个特定的图像合并所有的行

                 image  xmin  ymin  xmax  ymax       label
0   bookstore_video0_40.jpg   763   899   806   940  pedestrian
3   bookstore_video0_40.jpg  1026   754  1075   797  pedestrian
4   bookstore_video0_40.jpg   868   770   927   822       biker
5   bookstore_video0_40.jpg   413  1010   433  1040  pedestrian
21  bookstore_video0_80.jpg   866   278   917   328  pedestrian
22  bookstore_video0_80.jpg   761   825   820   865       biker

我想的是,也许可以把它做成一个单级嵌套字典,注意,我对任何解决方案都持开放态度,而不是像这样固定在字典里

{'bookstore_video0_40.jpg': {'xmin': 1066, 'ymin': 802, 'xmax': 1093, 'ymax': 829, 'label': 'pedestrian'}
但所有行数据都以图像名称为键。我的最终目标是将其传递给一个函数,该函数将按顺序将每一行的数据写入一个文件。
尽管如此,我还是不知道如何将数据分组成块。我做了groupby“图像”,但我不知道如何将这些数据转换成我想要的东西。有人有想法吗?我很确定这很容易,我环顾了四周,但我看到的大多数回答都是针对更复杂的问题。提前感谢。

您可能需要使用dict,并在groupby上应用元组/列表:

这个怎么样

new_dict = df.set_index('image').stack().groupby('image').apply(list).to_dict()


print(new_dict)
{'bookstore_video0_40.jpg': [763,
  899,
  806,
  940,
  'pedestrian',
  1026,
  754,
  1075,
  797,
  'pedestrian',
  868,
  770,
  927,
  822,
  'biker',
  413,
  1010,
  433,
  1040,
  'pedestrian'],
 'bookstore_video0_80.jpg': [866,
  278,
  917,
  328,
  'pedestrian',
  761,
  825,
  820,
  865,
  'biker']}

我想这样做是作为一个评论,而不是一个答案,但问题太长了:


我写过一个voc作家。我只需要能够传递数据,这样我就可以在上面循环。我有一个不同的数据集,在那里我做了一些类似的事情,但数据已经是一种易于使用的形式。对于我的项目,我花了很多时间编辑、清理、转换等数据。对我来说不好玩这里是一个基于您的示例的工作示例,除了读取实际的XML文件。非常感谢你。我怀疑你的答案会有用,因为这是机器视觉的人在切割已经注释过的4K图像时遇到的问题

import sys
import glob
import numpy as np
import pandas as pd
from lxml import etree
from pathlib import Path, PurePosixPath
from xml.etree import ElementTree as ET

df = pd.DataFrame(dict(
    image = '40.jpg 40.jpg 40.jpg 40.jpg 80.jpg 80.jpg'.split(),
    xmin = [763, 1026, 868, 413, 866, 761],
    ymin = [899, 754, 770, 1010, 278, 825],
    xmax = [806, 1075, 927, 433, 917, 820],
    ymax = [940, 797, 822, 1040, 328, 865],
    label = 'pedestrian pedestrian biker pedestrian pedestrian biker'.split(),
))


for img in df['image'].unique():
    img_df = df[df['image']==img].drop(columns = 'image').reset_index()
    boxes = range(img_df.shape[0])
    print(img, '\n', img_df)

    # Ideally your custom voc writer can be inited here
    # with something like:
    image = img
    # v_writer = VocWriter(f'path/{img[:-4]}.xml')
    print("New custom VOC Writer instance inited here!")

    depth = 3
    filepath = PurePosixPath('image')
    annotation = ET.Element('annotation')
    ET.SubElement(annotation, 'folder').text = str(image)
    ET.SubElement(annotation, 'filename').text = str(image)
    ET.SubElement(annotation, 'segmented').text = '0'
    size = ET.SubElement(annotation, 'size')
    ET.SubElement(size, 'width').text = str('0')
    ET.SubElement(size, 'height').text = str('0')
    ET.SubElement(size, 'depth').text = str('3')

    for box in boxes:
        xmin = img_df.loc[box,'xmin']
        ymin = img_df.loc[box,'ymin']
        xmax = img_df.loc[box,'xmax']
        ymax = img_df.loc[box,'ymax']
        label = img_df.loc[box,'label']
        print(xmin, ymin, xmax, ymax)

        # Inside of this loop, 
        # you can add each box to your VocWriter object
        # something like:

        ob = ET.SubElement(annotation, 'object')
        ET.SubElement(ob, 'name').text = str(img_df.loc[box,'label'])
        ET.SubElement(ob, 'pose').text = 'Unspecified'
        ET.SubElement(ob, 'truncated').text = '0'
        ET.SubElement(ob, 'difficult').text = '0'
        bbox = ET.SubElement(ob, 'bndbox')
        ET.SubElement(bbox, 'xmin').text = str(img_df.loc[box,'xmin'])
        ET.SubElement(bbox, 'ymin').text = str(img_df.loc[box,'ymin'])
        ET.SubElement(bbox, 'xmax').text = str(img_df.loc[box,'xmax'])
        ET.SubElement(bbox, 'ymax').text = str(img_df.loc[box,'ymax'])

    # Once you exit that inner loop,
    # you can save your data to your .xml file
    # with something like:

    # v_writer.save(f'path/{img[:-4]}.xml')
    print(".xml file saved here!")

    fileName = str(img)
    tree = ET.ElementTree(annotation)
    tree.write("./mergedxml/" + fileName + ".xml", encoding='utf8')


你从哪里知道ie.bookstore\u video0\u 40.jpg的正确属性是什么?因为该图像有多个实例,你会为Xmini选择什么?如果你的最终目标是将所有这些数据写入一个文件,为什么不保持表格式,并通过或或pandas拥有的许多其他文件写入方法之一将其写入csv或xls文件?分组是为了减少文件大小还是什么?我必须将其写入VOC XML格式,其中每一行都是同一图像的边界框。这是机器学习中的一种标准方法,其中图像可能有1到N个边界框。有些应用程序会将该数据转换为类似于您提到的表格数据格式的CSV。其他不同风格的XML。使用XML时,只需编写一次图像名称,然后在不同分支下编写构成该边界框的所有行。当你完成后,你为下一张图片制作了一个新的文件等。对不起,不清楚。你看过这个吗?我以前从来没有用过,但也许你可以用双for循环。第一个循环类似于df['image']中的img。唯一:然后执行writer=Writerpath,width,height部分,然后内部循环将是df[df['image']==img]中的bb,在该循环内部,您可以调用该writer.addObjectbb['label'],bb['xmin'],bb['ymin'],bb['xmax'],bb['xmin'],最后是writer.save'path/to/img.xml'部分,我已经编写了一个voc编写器。我只需要能够传递数据,这样我就可以在上面循环。我有一个不同的数据集,在那里我做了一些类似的事情,但数据已经是一种易于使用的形式。对于我的项目,我花了很多时间编辑、清理、转换等数据。对我来说不好玩哦这很接近。它包括图像名称,但我可以处理。谢谢你的教育,克里斯蒂安。看起来也会有用的。由于列标签已经消失,我必须跟踪位置,但这似乎是另一个好的解决方案。我两个都要玩。非常感谢你,马修。这正是我想要的,而且比我预期的要多得多。给我一点时间,或者明天之前,如果可以的话,我会发布整个工作代码。我真的很感激这一点,因为我从未见过这个python导师网站。谢谢你通知我!看起来很棒!我的想法是1。看起来您将要保存这些.xml文件的文件名将包含原始的.jpg扩展名,即bookstore\u video0\u 40。jpg将保存为bookstore\u video0\u 40.jpg.xml。这更像是一件装饰性的事情,因为操作系统只应使用最后一个扩展名来识别文件类型,但是我认为如果在保存文件之前删除.jpg会更好一些。2您经常调用str函数,这可能是不必要的,但可能我误解了DataFrame中数据的对象类型或编写器的工作方式。
import sys
import glob
import numpy as np
import pandas as pd
from lxml import etree
from pathlib import Path, PurePosixPath
from xml.etree import ElementTree as ET

df = pd.DataFrame(dict(
    image = '40.jpg 40.jpg 40.jpg 40.jpg 80.jpg 80.jpg'.split(),
    xmin = [763, 1026, 868, 413, 866, 761],
    ymin = [899, 754, 770, 1010, 278, 825],
    xmax = [806, 1075, 927, 433, 917, 820],
    ymax = [940, 797, 822, 1040, 328, 865],
    label = 'pedestrian pedestrian biker pedestrian pedestrian biker'.split(),
))


for img in df['image'].unique():
    img_df = df[df['image']==img].drop(columns = 'image').reset_index()
    boxes = range(img_df.shape[0])
    print(img, '\n', img_df)

    # Ideally your custom voc writer can be inited here
    # with something like:
    image = img
    # v_writer = VocWriter(f'path/{img[:-4]}.xml')
    print("New custom VOC Writer instance inited here!")

    depth = 3
    filepath = PurePosixPath('image')
    annotation = ET.Element('annotation')
    ET.SubElement(annotation, 'folder').text = str(image)
    ET.SubElement(annotation, 'filename').text = str(image)
    ET.SubElement(annotation, 'segmented').text = '0'
    size = ET.SubElement(annotation, 'size')
    ET.SubElement(size, 'width').text = str('0')
    ET.SubElement(size, 'height').text = str('0')
    ET.SubElement(size, 'depth').text = str('3')

    for box in boxes:
        xmin = img_df.loc[box,'xmin']
        ymin = img_df.loc[box,'ymin']
        xmax = img_df.loc[box,'xmax']
        ymax = img_df.loc[box,'ymax']
        label = img_df.loc[box,'label']
        print(xmin, ymin, xmax, ymax)

        # Inside of this loop, 
        # you can add each box to your VocWriter object
        # something like:

        ob = ET.SubElement(annotation, 'object')
        ET.SubElement(ob, 'name').text = str(img_df.loc[box,'label'])
        ET.SubElement(ob, 'pose').text = 'Unspecified'
        ET.SubElement(ob, 'truncated').text = '0'
        ET.SubElement(ob, 'difficult').text = '0'
        bbox = ET.SubElement(ob, 'bndbox')
        ET.SubElement(bbox, 'xmin').text = str(img_df.loc[box,'xmin'])
        ET.SubElement(bbox, 'ymin').text = str(img_df.loc[box,'ymin'])
        ET.SubElement(bbox, 'xmax').text = str(img_df.loc[box,'xmax'])
        ET.SubElement(bbox, 'ymax').text = str(img_df.loc[box,'ymax'])

    # Once you exit that inner loop,
    # you can save your data to your .xml file
    # with something like:

    # v_writer.save(f'path/{img[:-4]}.xml')
    print(".xml file saved here!")

    fileName = str(img)
    tree = ET.ElementTree(annotation)
    tree.write("./mergedxml/" + fileName + ".xml", encoding='utf8')