是否存在Pandas的数据集文件格式,可以在多个列上进行索引(即“数据库索引”),和/或可以廉价更新?

时间:2018-03-02 00:28:39

标签: python pandas

我正在为大于内存的数据集构建一个交互式浏览器和编辑器,稍后将使用Pandas进行处理。因此,我需要在几个列上有索引,数据集将被交互式排序或过滤(数据库索引,而不是Pandas索引),我希望数据集文件格式支持廉价编辑而不重写大部分文件。就像数据库一样,只有我希望以后能够以Pandas兼容的格式发送文件而不导出。

所以,我想知道Pandas支持的任何格式是什么:

  • 可以选择在多个列上构建数据库索引(用于排序和过滤)
  • 可以“就地”更新或以其他方式更新,而无需移动剩余的记录
  • 优选上述两者

我有什么选择?

我是Pandas中的一个完整的菜鸟,到目前为止,似乎大多数格式都是序列化的顺序记录,就像CSV一样,最多可以在一列上进行排序或索引。如果没有更好的结果,我将不得不自己在外部构建索引并在导出数据集之前手动处理编辑的行,或者将整个数据集转储进出数据库 - 但我宁愿避免这两者。

编辑:更具体地说,it appears that Parquet has upper/lower bounds recorded for each column in each data page,我想知道这些是否可以用作索引排序,以加快对任意列的排序,或者其他格式是否具有相似的功能。

2 个答案:

答案 0 :(得分:4)

我认为镶木地板确实是这种情况的好格式。它很好地映射到pandas数据帧的表格性质,将最常见的数据存储在高效的二进制表示中(使用可选的压缩),并且是标准的可移植格式。此外,它允许您只加载您需要的那些列或“行组”(块)。后者是你问题的关键所在。

Pandas'.to_parquet()将自动存储与数据框索引相关的元数据,并按照您的建议创建列max / min元数据。如果您使用fastparquet后端,则可以在加载时使用filters=关键字来仅选择部分行组(这不会过滤 行组中的)< / p>

pd.read_parquet('split', filters=[('cat', '==', '1')], 
    engine='fastparquet')

(仅选择字段'cat'的某些值等于'1'的行组 如果您在写入时使用了基于目录的分区,这可能会特别有效,例如

out.to_parquet('another_dataset.parq', partition_on=['cat'], 
    engine='fastparquet', file_scheme='hive')

其中一些选项仅记录在fastparquet docs中,并且该库的API可能比通过pandas方法实现的更多;我不确定箭头后端如何实现这些选项。

进一步注意,您可能希望使用dask转换/ / read_parquet方法来读取/保存数据帧。如果索引是1D并且执行等效的filters=操作,则当您对索引执行过滤操作时,Dask会自动仅加载光盘上数据的相关部分。 Dask用于处理不易融入内存的数据,并且可以并行执行计算。

(回答上面的一些评论:Pandas-SQL交互通常高效,除非您可以将计算的更难部分推送到快速数据库后端 - 在这种情况下你不要真的有问题吗?

编辑一些特定的注释:

  • 镶木地板通常不用于原子记录更新;但是你可以写一些整体的块(不是通过pandas API - 我认为这适用于所有的写入格式方法)
  • 您所说的“索引”与熊猫索引不同,但我认为上述信息可能表明镶木地板中的索引类型对您仍然有用。

答案 1 :(得分:0)

如果您决定采用数据库路线,SQLite是完美的,因为它已经附带Python,驱动程序api在Python的standard library中,并且fie格式与平台无关。我将它用于我的所有个人项目。

示例已从此tutorial on Pandas + sqlite3pandas.io documentation

进行了修改
# Code to create the db
import sqlite3
import pandas as pd

# Create a data frame
df = pd.DataFrame(dict(col1=[1,2,3], col2=['foo', 'bar', 'baz']))
df.index = ('row{}'.format(i) for i in range(df.shape[0]))

# Connect to your database
conn = sqlite3.connect("data/your_database.sqlite")

# Write the data to your table (overwrite old data)
df.to_sql('your_table', conn, if_exists='replace')

# Add more data
new_rows = pd.DataFrame(
    dict(col1=[3, 4], col2=['notbaz', 'grunt']),
    index=('row2', 'row3')
)
new_rows.to_sql('your_table', conn, if_exists='append')  # `append`

如果你需要更复杂的东西,这部分是旁白的:

# (oops - duplicate row 2)
# also ... need to quote "index" column name because it's a keyword.
query = 'SELECT * FROM your_table WHERE "index" = ?'
pd.read_sql(query, conn, params=('row2',))
#   index  col1    col2
# 0  row2     3     baz
# 1  row2     3  notbaz

# For more complex queries use pandas.io.sql
from pandas.io import sql
query = 'DELETE FROM your_table WHERE "col1" = ? AND "col2" = ?'
sql.execute(query, conn, params=(3, 'notbaz'))
conn.commit()

# close
conn.close()

当您或协作者想要从数据库中读取数据时,只需发送它们即可 文件 data/your_database.sqlite 和此代码:

# Code to access the db
import sqlite3
import pandas as pd

# Connect to your database
conn = sqlite3.connect("data/your_database.sqlite")

# Load the data into a DataFrame
query = 'SELECT * FROM your_table WHERE col1 BETWEEN ? and ?'
df = pd.read_sql_query(query, conn, params=(1,3))
#    index  col1 col2
#  0  row0     1  foo
#  1  row1     2  bar
#  2  row2     3  baz