在同一环境中使用 CLI Versus 可执行文件从 parquet 读取 DataFrame 时的不同行为
Different behavior while reading DataFrame from parquet using CLI Versus executable on same environment
请将以下程序视为Minimal Reproducible Example -MRE:
import pandas as pd
import pyarrow
from pyarrow import parquet
def foo():
print(pyarrow.__file__)
print('version:',pyarrow.cpp_version)
print('-----------------------------------------------------')
df = pd.DataFrame({'A': [1,2,3], 'B':['dummy']*3})
print('Orignal DataFrame:\n', df)
print('-----------------------------------------------------')
_table = pyarrow.Table.from_pandas(df)
parquet.write_table(_table, 'foo')
_table = parquet.read_table('foo', columns=[]) #passing empty list to columns arg
df = _table.to_pandas()
print('After reading from file with columns=[]:\n', df)
print('-----------------------------------------------------')
print('Not passing [] to columns parameter')
_table = parquet.read_table('foo') #Not passing any list
df = _table.to_pandas()
print(df)
print('-----------------------------------------------------')
x = input('press any key to exit: ')
if __name__=='__main__':
foo()
当我从 console/IDE 运行 时,它会读取 columns=[]
的全部数据:
(env) D:\foo>python foo.py
D:\foo\env\lib\site-packages\pyarrow\__init__.py
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
但是当我从使用 Pyinstaller 创建的可执行文件 运行 时,它没有读取 columns=[]
:
的数据
E:\foo\dist\foo\pyarrow\__init__.pyc
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
Empty DataFrame
Columns: []
Index: [0, 1, 2]
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
如您所见,传递 columns=[]
会在可执行文件中给出空数据框,但在 运行 直接 python 文件时不存在此行为,我不确定为什么对于同一环境中的同一代码,存在这两种不同的行为。
正在查看 source code at GitHub 中 parquet.read_table
的文档字符串:
columns: list
If not None, only these columns will be read from the file. A column
name may be a prefix of a nested field, e.g. 'a' will select 'a.b',
'a.c', and 'a.d.e'.
read_table 进一步调用 Scanner
dataset.read
that calls _dataset.to_table
which returns call to self.scanner
which then returns call to static method from_dataset
class。
到处都是None
作为columns
参数的默认值,如果None
和[]
在python中直接转换为布尔值,它们确实都是 False
,但是如果 []
与 None
进行检查,那么它将是 False
,但是如果它获取所有列columns=[]
因为布尔值的计算结果为 False
,或者因为列表为空所以它根本不读取任何列。
但是,为什么从命令 line/IDE 中 运行 宁它的行为与从使用 Pyinstaller 为相同版本的 Pyarrow 创建的可执行文件 运行 宁它时的行为不同?
我所在的环境:
- Python版本:3.7.6
- Pyinstaller 版本:4.2
- Pyarrow 版本:3.0.0
- Windows 10 个 64 位 OS
如果你想试一试,这里是供你参考的规格文件(你需要更改pathex
参数):
foo.spec
# -*- mode: python ; coding: utf-8 -*-
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
block_cipher = None
a = Analysis(['foo.py'],
pathex=['D:\foo'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='foo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='foo')
pyarrow.parquet.read_table
的 pyarrow 文档可能不清楚。我提出 ARROW-13436 来澄清这一点。
从某些测试来看,行为似乎在某些时候从无列更改为所有列,然后又变回(在 4.0 中)无列。我认为没有专栏是正确的行为。
所以我猜测您的可执行文件使用的 pyarrow 版本与您的 IDE 不同。您通常可以通过 运行...
来确认这一点
import pyarrow
print(pyarrow.__file__)
print(pyarrow.cpp_version)
...在两种环境下比较结果。
在@ThePyGuy(OP)评论后,找到了原因。
原因是,当他在 IDE 上 运行 时,他 运行 它在环境中,而当他 运行 在命令行上时,它在环境之外。和不同的 DLL 文件。所以修复是将 pyarrow 包复制到环境之外,它给出了相同的结果。
感谢@U12-Forward 协助我调试问题。
经过一些研究和调试,并探索了库程序文件,我发现 pyarrow 使用 _ParquetDatasetV2
和 ParquetDataset
函数,这两个函数本质上是从 parquet 文件读取数据的不同函数,_ParquetDatasetV2
被用作 legacy_mode
,即使这些函数是在 pyarrow.parquet 模块中定义的,它们来自 pyarrow 的 Dataset
模块,它在使用创建的可执行文件中丢失Pyinstaller.
当我将 pyarrow.Dataset
添加为隐藏导入并创建构建时,由于 Dataset
模块使用的几个缺少依赖项,exe 在执行时会引发 ModuleNotFoundError
。为了解决它,我将环境中的所有 .py
文件添加到隐藏的导入并再次创建构建,最后它成功了,我的意思是我能够在两者中观察到相同的行为环境。
修改后的spec
文件修改后如下所示:
# -*- mode: python ; coding: utf-8 -*-
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
block_cipher = None
a = Analysis(['foo.py'],
pathex=['D:\foo'],
binaries=[],
datas=[],
hiddenimports=['pyarrow.benchmark', 'pyarrow.cffi', 'pyarrow.compat', 'pyarrow.compute', 'pyarrow.csv', 'pyarrow.cuda', 'pyarrow.dataset', 'pyarrow.feather', 'pyarrow.filesystem', 'pyarrow.flight', 'pyarrow.fs', 'pyarrow.hdfs', 'pyarrow.ipc', 'pyarrow.json', 'pyarrow.jvm', 'pyarrow.orc', 'pyarrow.pandas_compat', 'pyarrow.parquet', 'pyarrow.plasma', 'pyarrow.serialization', 'pyarrow.types', 'pyarrow.util', 'pyarrow._generated_version', 'pyarrow.__init__'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='foo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='foo')
此外,为了创建构建,我使用 --paths
参数包含了虚拟环境的路径:
pyinstaller --path D:\foo\env\Lib\site-packages foo.spec
执行上述步骤后执行:
E:\foo\dist\foo\pyarrow\__init__.pyc
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
确实没有任何地方提到 columns=[]
的期望行为,但是通过@Pace 查看 ARROW-13436 opened in pyarrow,似乎 columns=[]
的期望行为是阅读根本没有数据列,但它不是官方构造,因此它可能是 pyarrow 3.0.0 本身的错误。
请将以下程序视为Minimal Reproducible Example -MRE:
import pandas as pd
import pyarrow
from pyarrow import parquet
def foo():
print(pyarrow.__file__)
print('version:',pyarrow.cpp_version)
print('-----------------------------------------------------')
df = pd.DataFrame({'A': [1,2,3], 'B':['dummy']*3})
print('Orignal DataFrame:\n', df)
print('-----------------------------------------------------')
_table = pyarrow.Table.from_pandas(df)
parquet.write_table(_table, 'foo')
_table = parquet.read_table('foo', columns=[]) #passing empty list to columns arg
df = _table.to_pandas()
print('After reading from file with columns=[]:\n', df)
print('-----------------------------------------------------')
print('Not passing [] to columns parameter')
_table = parquet.read_table('foo') #Not passing any list
df = _table.to_pandas()
print(df)
print('-----------------------------------------------------')
x = input('press any key to exit: ')
if __name__=='__main__':
foo()
当我从 console/IDE 运行 时,它会读取 columns=[]
的全部数据:
(env) D:\foo>python foo.py
D:\foo\env\lib\site-packages\pyarrow\__init__.py
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
但是当我从使用 Pyinstaller 创建的可执行文件 运行 时,它没有读取 columns=[]
:
E:\foo\dist\foo\pyarrow\__init__.pyc
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
Empty DataFrame
Columns: []
Index: [0, 1, 2]
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
如您所见,传递 columns=[]
会在可执行文件中给出空数据框,但在 运行 直接 python 文件时不存在此行为,我不确定为什么对于同一环境中的同一代码,存在这两种不同的行为。
正在查看 source code at GitHub 中 parquet.read_table
的文档字符串:
columns: list
If not None, only these columns will be read from the file. A column name may be a prefix of a nested field, e.g. 'a' will select 'a.b', 'a.c', and 'a.d.e'.
read_table 进一步调用 Scanner
dataset.read
that calls _dataset.to_table
which returns call to self.scanner
which then returns call to static method from_dataset
class。
到处都是None
作为columns
参数的默认值,如果None
和[]
在python中直接转换为布尔值,它们确实都是 False
,但是如果 []
与 None
进行检查,那么它将是 False
,但是如果它获取所有列columns=[]
因为布尔值的计算结果为 False
,或者因为列表为空所以它根本不读取任何列。
但是,为什么从命令 line/IDE 中 运行 宁它的行为与从使用 Pyinstaller 为相同版本的 Pyarrow 创建的可执行文件 运行 宁它时的行为不同?
我所在的环境:
- Python版本:3.7.6
- Pyinstaller 版本:4.2
- Pyarrow 版本:3.0.0
- Windows 10 个 64 位 OS
如果你想试一试,这里是供你参考的规格文件(你需要更改pathex
参数):
foo.spec
# -*- mode: python ; coding: utf-8 -*-
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
block_cipher = None
a = Analysis(['foo.py'],
pathex=['D:\foo'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='foo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='foo')
pyarrow.parquet.read_table
的 pyarrow 文档可能不清楚。我提出 ARROW-13436 来澄清这一点。
从某些测试来看,行为似乎在某些时候从无列更改为所有列,然后又变回(在 4.0 中)无列。我认为没有专栏是正确的行为。
所以我猜测您的可执行文件使用的 pyarrow 版本与您的 IDE 不同。您通常可以通过 运行...
来确认这一点import pyarrow
print(pyarrow.__file__)
print(pyarrow.cpp_version)
...在两种环境下比较结果。
在@ThePyGuy(OP)评论后,找到了原因。
原因是,当他在 IDE 上 运行 时,他 运行 它在环境中,而当他 运行 在命令行上时,它在环境之外。和不同的 DLL 文件。所以修复是将 pyarrow 包复制到环境之外,它给出了相同的结果。
感谢@U12-Forward 协助我调试问题。
经过一些研究和调试,并探索了库程序文件,我发现 pyarrow 使用 _ParquetDatasetV2
和 ParquetDataset
函数,这两个函数本质上是从 parquet 文件读取数据的不同函数,_ParquetDatasetV2
被用作 legacy_mode
,即使这些函数是在 pyarrow.parquet 模块中定义的,它们来自 pyarrow 的 Dataset
模块,它在使用创建的可执行文件中丢失Pyinstaller.
当我将 pyarrow.Dataset
添加为隐藏导入并创建构建时,由于 Dataset
模块使用的几个缺少依赖项,exe 在执行时会引发 ModuleNotFoundError
。为了解决它,我将环境中的所有 .py
文件添加到隐藏的导入并再次创建构建,最后它成功了,我的意思是我能够在两者中观察到相同的行为环境。
修改后的spec
文件修改后如下所示:
# -*- mode: python ; coding: utf-8 -*-
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
block_cipher = None
a = Analysis(['foo.py'],
pathex=['D:\foo'],
binaries=[],
datas=[],
hiddenimports=['pyarrow.benchmark', 'pyarrow.cffi', 'pyarrow.compat', 'pyarrow.compute', 'pyarrow.csv', 'pyarrow.cuda', 'pyarrow.dataset', 'pyarrow.feather', 'pyarrow.filesystem', 'pyarrow.flight', 'pyarrow.fs', 'pyarrow.hdfs', 'pyarrow.ipc', 'pyarrow.json', 'pyarrow.jvm', 'pyarrow.orc', 'pyarrow.pandas_compat', 'pyarrow.parquet', 'pyarrow.plasma', 'pyarrow.serialization', 'pyarrow.types', 'pyarrow.util', 'pyarrow._generated_version', 'pyarrow.__init__'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='foo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='foo')
此外,为了创建构建,我使用 --paths
参数包含了虚拟环境的路径:
pyinstaller --path D:\foo\env\Lib\site-packages foo.spec
执行上述步骤后执行:
E:\foo\dist\foo\pyarrow\__init__.pyc
version: 3.0.0
-----------------------------------------------------
Orignal DataFrame:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
After reading from file with columns=[]:
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
Not passing [] to columns parameter
A B
0 1 dummy
1 2 dummy
2 3 dummy
-----------------------------------------------------
press any key to exit:
确实没有任何地方提到 columns=[]
的期望行为,但是通过@Pace 查看 ARROW-13436 opened in pyarrow,似乎 columns=[]
的期望行为是阅读根本没有数据列,但它不是官方构造,因此它可能是 pyarrow 3.0.0 本身的错误。