从嵌套列表创建 DataFrame 时,我可以访问 read_csv() 的数据类型推理引擎吗?
Can I access read_csv()'s dtype inference engine when creating a DataFrame from a nested list?
这是从与 piRSquared 的讨论中得出的,我发现 read_csv
似乎有自己的类型推断方法,这些方法在获得正确类型的能力方面似乎更广泛。在丢失数据的情况下,它似乎也更容错,选择 NaN
而不是抛出 ValueError
作为其默认行为。
在很多情况下,我的工作完全可以接受推断的数据类型,但在实例化 DataFrame
或 API 中的其他任何地方时,此功能似乎并未公开我可以找到,这意味着我必须不必要地手动处理 dtypes
。如果您有数百列,这可能会很乏味。我能找到的最接近的是 convert_objects()
但在这种情况下它不处理布尔值。我可以使用的替代方法是转储到磁盘并将其读回,这是非常低效的。
下面的示例说明了 read_csv
的默认行为与设置 dtype 的常规方法的默认行为(在 V 0.20.3
中正确)。有没有办法在不转储到磁盘的情况下访问 read_csv
的类型推断?更一般地说,read_csv
有这样的行为的原因吗?
示例:
import numpy as np
import pandas as pd
import csv
data = [['string_boolean', 'numeric', 'numeric_missing'],
['FALSE', 23, 50],
['TRUE', 19, 12],
['FALSE', 4.8, '']]
with open('my_csv.csv', 'w') as outfile:
writer = csv.writer(outfile)
writer.writerows(data)
# Reading from CSV
df = pd.read_csv('my_csv.csv')
print(df.string_boolean.dtype) # Automatically converted to bool
print(df.numeric.dtype) # Float, as expected
print(df.numeric_missing.dtype) # Float, doesn't care about empty string
# Creating directly from list without supplying datatypes
df2 = pd.DataFrame(data[1:], columns=data[0])
df2.string_boolean = df2.string_boolean.astype(bool) # Doesn't work - ValueError
df2.numeric_missing = df2.numeric_missing.astype(np.float64) # Doesn't work
# Creating but forcing dtype doesn't work
df3 = pd.DataFrame(data[1:], columns=data[0],
dtype=[bool, np.float64, np.float64])
# The working method
df4 = pd.DataFrame(data[1:], columns=data[0])
df4.string_boolean.map({'TRUE': True, 'FALSE': False})
df4.numeric_missing = pd.to_numeric(df4.numeric_missing)
一种解决方案是使用 StringIO
对象。唯一的区别是它将所有数据保存在内存中,而不是写入磁盘并读回。
代码如下(注意:Python3!):
import numpy as np
import pandas as pd
import csv
from io import StringIO
data = [['string_boolean', 'numeric', 'numeric_missing'],
['FALSE', 23, 50],
['TRUE', 19, 12],
['FALSE', 4.8, '']]
with StringIO() as fobj:
writer = csv.writer(fobj)
writer.writerows(data)
fobj.seek(0)
df = pd.read_csv(fobj)
print(df.head(3))
print(df.string_boolean.dtype) # Automatically converted to bool
print(df.numeric.dtype) # Float, as expected
print(df.numeric_missing.dtype) # Float, doesn't care about empty string
with StringIO() as fobj
并不是必需的:fobj = String()
也可以。由于上下文管理器将在其范围之外关闭 StringIO()
对象,因此 df = pd.read_csv(fobj)
必须在其中。
另请注意 fobj.seek(0)
,这是另一个必要条件,因为您的解决方案只是关闭并重新打开文件,这将自动将文件指针设置为文件的开头。
关于 Python 2 与 Python 3
的说明
我实际上试图使上面的代码 Python 2/3 兼容。由于以下原因,这变得一团糟:Python 2 有一个 io
模块,就像 Python 3 一样,其 StringIO
class 使所有内容成为 unicode(也在 Python 2 中;在 Python 3 中,当然是默认值)。
这很好,除了 Python 2 中的 csv
编写器模块 不 unicode 兼容。
因此,替代方法是使用(较旧的)Python 2 (c)StringIO
模块,例如:
try:
from cStringIO import StringIO
except ModuleNotFoundError: # Python 3
from io import StringIO
在 Python 2 中是纯文本,在 Python 3 中是 unicode。
除了现在,cStringIO.StringIO
没有上下文管理器,with
语句将失败。正如我所提到的,这并不是真正必要的,但我尽可能地让事情接近你的原始代码。
换句话说,我找不到一个很好的方法来保持接近原始代码而没有荒谬的黑客攻击。
我也考虑过完全避免使用 CSV 编写器,这导致:
text = '\n'.join(','.join(str(item).strip("'") for item in items)
for items in data)
with StringIO(text) as fobj:
df = pd.read_csv(fobj)
这可能更整洁(虽然不太清楚),和 Python 2/3 兼容。 (我不希望它适用于 csv
模块可以处理的所有内容,但在这里它工作正常。)
为什么不能 pd.DataFrame(...)
进行转换?
这里,我只能推测。
我认为原因是当输入是 Python 对象(字典、列表)时,输入是已知的,并且掌握在程序员手中。因此,该输入不太可能甚至不合逻辑地包含 'FALSE'
或 ''
等字符串。相反,它通常包含对象 False
和 np.nan
(或 math.nan
),因为程序员已经处理了(字符串)翻译。
而对于文件(CSV 或其他),输入可以是任何内容:您的同事可能会发送一个 Excel CSV 文件,或者其他人会向您发送一个 Gnumeric CSV 文件。我不知道 CSV 文件的标准化程度如何,但您可能需要一些代码来允许异常,以及将字符串转换为 Python (NumPy) 格式的总体代码。
所以从这个意义上说,期望 pd.DAtaFrame(...)
接受任何东西实际上是不合逻辑的:相反,它 应该 接受格式正确的东西。
您可能会争论采用像您这样的列表的便捷方法,但列表不是 CSV 文件(它只是一堆字符,包括换行符)。另外,我希望 pd.read_csv
可以选择分块读取文件(甚至可能逐行读取),如果你用换行符代替字符串(你不能真正读取它),这会变得更难逐行,因为你必须在换行符上拆分它并将所有行保留在内存中。你已经在内存中的某个地方而不是在磁盘上拥有完整的字符串。但我离题了)。
此外,StringIO
技巧只是几行就可以精确地执行这个技巧。
这是从与 piRSquared read_csv
似乎有自己的类型推断方法,这些方法在获得正确类型的能力方面似乎更广泛。在丢失数据的情况下,它似乎也更容错,选择 NaN
而不是抛出 ValueError
作为其默认行为。
在很多情况下,我的工作完全可以接受推断的数据类型,但在实例化 DataFrame
或 API 中的其他任何地方时,此功能似乎并未公开我可以找到,这意味着我必须不必要地手动处理 dtypes
。如果您有数百列,这可能会很乏味。我能找到的最接近的是 convert_objects()
但在这种情况下它不处理布尔值。我可以使用的替代方法是转储到磁盘并将其读回,这是非常低效的。
下面的示例说明了 read_csv
的默认行为与设置 dtype 的常规方法的默认行为(在 V 0.20.3
中正确)。有没有办法在不转储到磁盘的情况下访问 read_csv
的类型推断?更一般地说,read_csv
有这样的行为的原因吗?
示例:
import numpy as np
import pandas as pd
import csv
data = [['string_boolean', 'numeric', 'numeric_missing'],
['FALSE', 23, 50],
['TRUE', 19, 12],
['FALSE', 4.8, '']]
with open('my_csv.csv', 'w') as outfile:
writer = csv.writer(outfile)
writer.writerows(data)
# Reading from CSV
df = pd.read_csv('my_csv.csv')
print(df.string_boolean.dtype) # Automatically converted to bool
print(df.numeric.dtype) # Float, as expected
print(df.numeric_missing.dtype) # Float, doesn't care about empty string
# Creating directly from list without supplying datatypes
df2 = pd.DataFrame(data[1:], columns=data[0])
df2.string_boolean = df2.string_boolean.astype(bool) # Doesn't work - ValueError
df2.numeric_missing = df2.numeric_missing.astype(np.float64) # Doesn't work
# Creating but forcing dtype doesn't work
df3 = pd.DataFrame(data[1:], columns=data[0],
dtype=[bool, np.float64, np.float64])
# The working method
df4 = pd.DataFrame(data[1:], columns=data[0])
df4.string_boolean.map({'TRUE': True, 'FALSE': False})
df4.numeric_missing = pd.to_numeric(df4.numeric_missing)
一种解决方案是使用 StringIO
对象。唯一的区别是它将所有数据保存在内存中,而不是写入磁盘并读回。
代码如下(注意:Python3!):
import numpy as np
import pandas as pd
import csv
from io import StringIO
data = [['string_boolean', 'numeric', 'numeric_missing'],
['FALSE', 23, 50],
['TRUE', 19, 12],
['FALSE', 4.8, '']]
with StringIO() as fobj:
writer = csv.writer(fobj)
writer.writerows(data)
fobj.seek(0)
df = pd.read_csv(fobj)
print(df.head(3))
print(df.string_boolean.dtype) # Automatically converted to bool
print(df.numeric.dtype) # Float, as expected
print(df.numeric_missing.dtype) # Float, doesn't care about empty string
with StringIO() as fobj
并不是必需的:fobj = String()
也可以。由于上下文管理器将在其范围之外关闭 StringIO()
对象,因此 df = pd.read_csv(fobj)
必须在其中。
另请注意 fobj.seek(0)
,这是另一个必要条件,因为您的解决方案只是关闭并重新打开文件,这将自动将文件指针设置为文件的开头。
关于 Python 2 与 Python 3
的说明我实际上试图使上面的代码 Python 2/3 兼容。由于以下原因,这变得一团糟:Python 2 有一个 io
模块,就像 Python 3 一样,其 StringIO
class 使所有内容成为 unicode(也在 Python 2 中;在 Python 3 中,当然是默认值)。
这很好,除了 Python 2 中的 csv
编写器模块 不 unicode 兼容。
因此,替代方法是使用(较旧的)Python 2 (c)StringIO
模块,例如:
try:
from cStringIO import StringIO
except ModuleNotFoundError: # Python 3
from io import StringIO
在 Python 2 中是纯文本,在 Python 3 中是 unicode。
除了现在,cStringIO.StringIO
没有上下文管理器,with
语句将失败。正如我所提到的,这并不是真正必要的,但我尽可能地让事情接近你的原始代码。
换句话说,我找不到一个很好的方法来保持接近原始代码而没有荒谬的黑客攻击。
我也考虑过完全避免使用 CSV 编写器,这导致:
text = '\n'.join(','.join(str(item).strip("'") for item in items)
for items in data)
with StringIO(text) as fobj:
df = pd.read_csv(fobj)
这可能更整洁(虽然不太清楚),和 Python 2/3 兼容。 (我不希望它适用于 csv
模块可以处理的所有内容,但在这里它工作正常。)
为什么不能 pd.DataFrame(...)
进行转换?
这里,我只能推测。
我认为原因是当输入是 Python 对象(字典、列表)时,输入是已知的,并且掌握在程序员手中。因此,该输入不太可能甚至不合逻辑地包含 'FALSE'
或 ''
等字符串。相反,它通常包含对象 False
和 np.nan
(或 math.nan
),因为程序员已经处理了(字符串)翻译。
而对于文件(CSV 或其他),输入可以是任何内容:您的同事可能会发送一个 Excel CSV 文件,或者其他人会向您发送一个 Gnumeric CSV 文件。我不知道 CSV 文件的标准化程度如何,但您可能需要一些代码来允许异常,以及将字符串转换为 Python (NumPy) 格式的总体代码。
所以从这个意义上说,期望 pd.DAtaFrame(...)
接受任何东西实际上是不合逻辑的:相反,它 应该 接受格式正确的东西。
您可能会争论采用像您这样的列表的便捷方法,但列表不是 CSV 文件(它只是一堆字符,包括换行符)。另外,我希望 pd.read_csv
可以选择分块读取文件(甚至可能逐行读取),如果你用换行符代替字符串(你不能真正读取它),这会变得更难逐行,因为你必须在换行符上拆分它并将所有行保留在内存中。你已经在内存中的某个地方而不是在磁盘上拥有完整的字符串。但我离题了)。
此外,StringIO
技巧只是几行就可以精确地执行这个技巧。