为什么 reticulate 会将 pandas dfs 列表转换为 r dfs 列表,但如果使用字典或嵌套列表则不会?

Why will reticulate convert a list of pandas dfs to a list of r dfs but not if using a dictionary or nested lists?

这是我第一次使用 reticulate。我有 20 个多页 pdf tables 我正在从 python 中使用 camelot 提取数据(它们并不简单 tables 所以我需要更强大的 table reader).它创建一个 table 列表(每个页面一个 table)并创建一个 TableList 对象。我能够遍历列表并将 table 转换为 pandas 数据帧。使用其中一个 pdf 执行此操作的示例:

tables2001 = camelot.read_pdf('2001.pdf', flavor='stream', pages='1-end')
df2001 = list()
for t in tables2001:
  df = t.df
  df2001.append(df)

然后我可以 return 到 r,rdf2001 <- py$df2001 给我一个 r 的列表 data.frames。

但是,如果我改为将 python 数据帧列表放入嵌套列表或包含列表的字典中,则 r 转换将不再有效,并且生成的嵌套列表仍包含 pandas data.frames。可以理解地手动转换其中一个 dfs 的尝试给出了这个:

Error in as.data.frame.default(rdf2001_nested[[1]]) : 
  cannot coerce class ‘c("pandas.core.frame.DataFrame", "pandas.core.generic.NDFrame", ’ to a data.frame

如果我从嵌套列表中将单个列表拉入 r,例如df2001_a <- py$df2001[1],转换为 r data.frames 的单个列表。我不能对字典做同样的事情,因为转换将键保留为列表,所以嵌套仍然存在。

使用字典的想法是在 r 中获取一个每年标识的命名列表,因为 tables 本身不包含该信息。我可以解决它,但是命名列表的字典对我来说是最清晰的方法,假设它可以工作。尝试嵌套列表是为了弄清楚转换问题是否只发生在字典中,而事实并非如此;它与任何类型的嵌套。

我想了解为什么会这样。 reticulate 只能转换单层列表吗?这是否有潜在的原因,或者只是没有添加该功能但理论上可以?

更新完整代码:

Pdf table 是 here。我提取了每年涉及刑事案件的页面,这就是页面被列为 1-end 的原因;每本有 14 页。 Python 代码 运行 和 repl_python() - 工作并给出我想要的列表和字典的结果:

import camelot
import pandas

# Lists
tables2001 = camelot.read_pdf('2001.pdf', flavor='stream', pages='1-end')
tables2002 = camelot.read_pdf('2002.pdf', flavor='stream', pages='1-end')
tables2003 = camelot.read_pdf('2003.pdf', flavor='stream', pages='1-end')

dflist = list()
tablelist=[tables2001,tables2002,tables2003,tables2004]
for t in tablelist:
  df = t.df
  dflist.append(df)
  
# Dictionary - I got help with this from someone who is knows python well
tables = { f'20{str(n).zfill(2)}': camelot.read_pdf(f'20{str(n).zfill(2)}.pdf',
flavor='stream', pages='1-end', table_regions=['50,580,780,50']) for n in range(1,3)}

dfdict = { k: [df.df for df in v] for k, v in tables.items() }

R代码:

library(reticulate)

# List
rdflist <- py$dflist

# Dictionary
rdfdict <- py$dfdict

rdflist 是 data.frames 的列表。 rdfdict 是一个命名的嵌套列表,包含 3 个列表(2001、2002、2003),每个列表有 14 个 pandas 数据帧,即在 r.

中不可用
class(rdflist[[1]])
[1] "data.frame"
class(rdfdict[[1]][[1]])
[1] "pandas.core.frame.DataFrame"        "pandas.core.generic.NDFrame"       
[3] "pandas.core.base.PandasObject"      "pandas.core.base.StringMixin"      
[5] "pandas.core.accessor.DirNamesMixin" "pandas.core.base.SelectionMixin"   
[7] "python.builtin.object"  

尝试将单个 df 强制为 data.frame:

as.data.frame(rdfdict[[1]][[1]])
Error in as.data.frame.default(rdfdict[[1]][[1]]) : 
  cannot coerce class ‘c("pandas.core.frame.DataFrame", "pandas.core.generic.NDFrame", ’ to a data.frame

比较两个版本,您 运行 字典版本的一些差异包括一个额外的参数,table_regions 和字典理解中的额外嵌套循环:[df.df for df in v](有趣的是没有在 Python).

中引发错误

考虑调整可比返回值的一致性。顺便说一句,在 Python 中,您还可以 运行 类似于字典理解的列表理解。

Python

import camelot 
import pandas as pd

# LIST COMPREHENSION
pydf_list = [
    [tbl.df for tbl in camelot.read_pdf(f'{yr}.pdf', flavor='stream', pages='1-end')]
    for yr in range(2001, 2004)
]

# DICT COMPREHENSION
pydf_dict = {
    str(yr): [tbl.df for tbl in camelot.read_pdf(f'{yr}.pdf', flavor='stream', pages='1-end')]
    for yr in range(2001, 2004)
}

R

library(reticulate)

reticulate::source_python("myscript.py")

# NESTED LIST 
rdf_list <- reticulate::py$pydf_list 

# NESTED NAMED LIST 
rdf_dict <- reticulate::py$pydf_dict

但是,正如您所指出的,我确实使用可重现的示例重现了有问题的 dict 到命名列表的转换。报告此问题,一位 suggestion 的维护者将使用 py_to_r:

rdf_dict2 <- lapply(rdf_dict, function(lst) lapply(lst, py_to_r))