Панды читают XML-файл с назначенным типом данных

Мой код:

df = pd.read_xml(
    path_or_buffer=PATH,
    xpath = "//Data",
    compression = "gzip"
)

Я использую функцию Pandas read_xml() для чтения данных формата xml.gz. Я использую версию Pandas 1.3.2. Когда я попытался прочитать данные, Панды прочитали данные неправильно.

Данные выглядят так, как показано ниже. И colA, и colB должны быть строкой.

1-й файл данных:

<Data>
 <colA>abc</colA>
 <colB>168E3</colB>
</Data>
<Data>
 <colA>def</colA>
</Data>

2-й файл данных:

<Data>
 <colA>ghi</colA>
 <colB>23456</colB>
</Data>
<Data>
 <colA>jkl</colA>
</Data>

Когда я использую функцию read_xml(), это выглядит следующим образом:

1-й фрейм данных:

colA: abc, def
colB: 168000.0, None

2-й фрейм данных:

colA: ghi, jkl
colB: 23456.0, None

Я хочу прочитать данные в формате string, но в пандах dtype нет аргумента 1.3.2. Я хочу знать:

  1. Как я могу прочитать данные с назначенным типом данных?
  2. Если в столбце отсутствуют данные, Pandas присвоит этому столбцу тип float. Как этого избежать или есть ли какая-либо настройка для настройки типа данных столбца с отсутствующим значением при чтении данных?

Обратите внимание, что я могу использовать только эту версию Pandas и не могу ее обновить.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
180
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Определите dtype столбцов в pandas: (NaN будет читаться как класс «NoneType», но вы можете использовать astype() позже.

import pandas as pd
from io import StringIO

xml_  = """\
<root>
<Data>
 <colA>abc</colA>
 <colB>168E3</colB>
</Data>
<Data>
 <colA>def</colA>
</Data>
</root>"""

file = StringIO(xml_)

df = pd.read_xml(file, xpath = ".//Data", dtype = {'colA': str, 'colB': str})
print(df.astype(str).applymap(type))

Выход:

            colA           colB
0  <class 'str'>  <class 'str'>
1  <class 'str'>  <class 'str'>

Спасибо за ответ, но в dtype нет аргумента в пользу read_xml(). pandas.pydata.org/pandas-docs/version/1.3.2/reference/api/…

Jonathan 08.07.2024 10:55

Но последний оператор с astyp() в любом случае должен работать.

Hermann12 08.07.2024 12:52

Это работает, но поскольку данные вначале анализируются неправильно (например, 168E3 -> 168000.0), даже astype() работает, они станут строкой 168000.0.

Jonathan 08.07.2024 18:05

Если возможно, перейдите на более новую версию панд. Я использую версию: 2.0.3.

Hermann12 08.07.2024 23:30

Спасибо за ваш комментарий, но я не могу обновиться до более новой версии. Я пытаюсь найти, есть ли какое-либо решение, которое может настроить тип данных по умолчанию/отключить его.

Jonathan 09.07.2024 07:15
Ответ принят как подходящий

Применение простого преобразования xslt для принудительного ввода строкового типа для каждого значения может сработать (у меня нет pandas 1.3.2 для тестирования, но работает на pandas 2.x). Кажется невозможным получить канонический ответ, учитывая ограничение версии. Альтернативой может быть анализ XML-документа с помощью lxml и заполнение кадра данных вручную.

Преобразование объединяет нечисловой символ, который позже удаляется.

XSLT

<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
    <xsl:template match = "node()">
        <xsl:copy>
            <xsl:apply-templates select = "node()"/>
            <xsl:value-of select = "'|'"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Питон

import pandas as pd
from io import StringIO

xml_  = """\
<root>
<Data>
 <colA>abc</colA>
 <colB>168E3</colB>
</Data>
<Data>
 <colA>def</colA>
</Data>
</root>"""

file = StringIO(xml_)

df = pd.read_xml(xml_, stylesheet = "/home/lmc/tmp/test.xslt" )
print(df.apply(lambda x: x.str[:-1]))

Результат

  colA   colB
0  abc  168E3
1  def   None

Исходный результат

   colA    colB
0  abc|  168E3|
1  def|    None

Если вы можете установить другие библиотеки, вы можете прочитать XML-файл как dict с помощью xmltodict, а затем использовать его для создания кадра данных pandas.

import gzip
import xmltodict
import pandas as pd

def read_xml_as_dict(gzip_path):
    with gzip.open(gzip_path, "r") as f:
        xml = f.read()
    return xmltodict.parse(xml)

data = read_xml_as_dict(PATH)
df = pd.DataFrame(data["root"]["Data"])

Если вы не можете установить xmltodict, вы можете разобрать его с помощью xml.etree.ElementTree аналогичным образом:

import gzip
import xml.etree.ElementTree as ET
import pandas as pd

def read_xml_as_dict(gzip_path):
    with gzip.open(gzip_path, "r") as f:
        xml = f.read()

    root = ET.fromstring(xml)
    data_list = []

    for item in root.findall("Data"):
        data_dict = {}
        for child in item:
            data_dict[child.tag] = child.text
        data_list.append(data_dict)

    return data_list

data = read_xml_as_dict(PATH)
df = pd.DataFrame(data)

Оба будут печатать:

  colA   colB
0  abc  168E3
1  def    NaN

Столбцы будут иметь dtype object:

df.info()
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   colA    2 non-null      object
 1   colB    1 non-null      object

Другие вопросы по теме