【问题标题】:csv file compression without using existing libraries in Pythoncsv文件压缩而不使用Python中的现有库
【发布时间】:2016-07-28 21:45:00
【问题描述】:

我正在尝试在不使用任何第三方或框架提供的压缩库的情况下压缩 .csv 文件。

我已经尝试过,我想怎么想,一切。 我查看了 Huffman,但由于不允许我使用该解决方案,我尝试自己做。

一个例子:

6NH8,F,A,0,60541567,60541567,78.78,20
6NH8,F,A,0,60541569,60541569,78.78,25
6AH8,F,B,0,60541765,60541765,90.52,1
QMH8,F,B,0,60437395,60437395,950.5,1

我制作了一个算法,计算每个字符并给出它们被使用的次数,并且取决于它们被使用了多少次。

',' --- 28  
'5' --- 18
'6' --- 17 
'0' --- 15
'7' --- 10  
'8' --- 8 
'4' --- 8  
'1' --- 8
'9' --- 6  
'.' --- 4
'3' --- 4 
'\n'--- 4 
'H' --- 4    
'F' --- 4
'2' --- 3 
'A' --- 3
'N' --- 2 
'B' --- 2 
'M' --- 1 
'Q' --- 1 

[(',', 0), ('5', 1), ('6', 2), ('0', 3), ('7', 4), ('8', 5), 
 ('4', 6), ('1', 7), ('9', 8), ('.', 9), ('3', 10), ('\n', 11), 
 ('H', 12), ('F', 13), ('2', 14), ('A', 15), ('N', 16), ('B', 17), 
 ('M', 18), ('Q', 19)]

所以我没有存储例如 ord('H') = 72,而是给 H 值 12,依此类推。

但是,当我将所有字符更改为我的值时,我生成的 cvs(>40MB) 仍然大于原始值(19MB)。

我什至尝试了将列表分为 2 的替代方法。即,将一行分成两行。

[6NH8,F,A,0,]
[60541567,60541567,78.78,20]

但仍然比我的“霍夫曼”版本更大,甚至更大。

问题: 任何人都对如何 1.读取一个.csv文件, 2.使用那个是库的东西。或第三者。 3.生成并写一个更小的.csv文件?

对于第 2 步,我不要求提供完整的计算解决方案,只是建议如何最小化文件,即将每个值写入一个列表?等等

谢谢

【问题讨论】:

  • 为什么不想使用现有的库?

标签: python csv compression


【解决方案1】:

目前尚不清楚您是需要创建通用压缩算法还是自定义压缩算法以合理地处理此类数据。

目前还不清楚输出应该是另一个 CSV、由可打印 ASCII 字符组成的字符串还是纯二进制数据。

我将假设我们正在讨论自定义算法和 CSV 输出。 (无论如何,同样的原则也适用于另一种输出格式。)

您的输入似乎格式正确,并且总是重复相同类型的字段:

0 '6NH8'     : 4-character code
1 'F'        : character
2 'A'        : character
3 '0'        : integer
4 '60541567' : integer \_ some kind of
5 '60541567' : integer /  timestamps?
6 '78.78'    : float
7 '20'       : integer

构建字典

查看第 0 列中使用了多少不同的代码,以及有多少“第 1 列”+“第 2 列”的不同组合。

如果经常使用相同的值,那么绝对值得构建只存储一次然后在压缩行中引用的字典。

例如:

column0_dictionary = [ '6NH8', '6AH8', 'QMH8' ]
column12_dictionary = [ 'FA', 'FB' ];

因此,6NH8 将被引用为 06AH8 被引用为 1,等等。

同样,F,A 将被引用为 0F,B 被引用为 1

以较短的格式编码时间戳

假设第 4 列和第 5 列确实是时间戳,一个快速的方法是存储最小值并从每个压缩行中的实际值中减去它。

minimum_timestamp = 60437395

因此,60541569 变为 60541569 - 60437395 = 104174。

示例输出

以下是将这两种简单方法应用于您的示例输入时得到的结果:

# header
6NH8,6AH8,QMH8
FA,FB
60437395
# payload data
0,0,0,104172,104172,78.78,20
0,0,0,104174,104174,78.78,25
1,1,0,104370,104370,90.52,1
2,1,0,0,0,950.5,1

如果事实证明它们对应于“某事的开始”和“某事的结束”,您还可以在第 5 列中存储第 5 列和第 4 列之间的差异。

按原样,压缩有效载荷的大小约为原始输入大小的 70%。 (请记住,当您有更多行时,标题的大小应该可以忽略不计。)

您的示例太短,无法检测其余字段的任何其他明显模式,但希望这些示例能给您一些想法。

更新

事实证明,时间戳以自午夜以来经过的毫秒数表示。所以它们可能均匀分布在 0-86399999 中,不可能减去最小值。

但是,这些数字可以以比其十进制值的 ASCII 表示更紧凑的方式编码。

最简单的方法是将它们转换为十六进制:

60541567 = 39BCA7F

稍微复杂一点的方法是用 Base64 编码:

  1. 将时间戳转换为其 4 字节表示(从 0 到 86399999 的所有值都适合 4 字节):

  2. 构建一个由4个对应字符组成的字符串,并用Base64编码。

例如:

60541567 = 03 9B CA 7F  # in hexadecimal and big-endian order

BASE64(CHR(0x03) + CHR(0x9B) + CHR(0xCA) + CHR(0x7F)) = A5vKfw
# here without the padding characters

【讨论】:

  • 谢谢您,您的解决方案有效,但我仍然对时间戳有疑问,在理解作业时您是绝对正确的。但是做最小时间戳的方法不起作用,因为有时时间戳是“68”,它是午夜后的毫秒。您知道另一种“最小化”时间戳的解决方案吗?在原始文件中有 500 000 行(19.1MB),当我重新存储你描述的没有时间戳的方式时,文件要小得多
  • 请查看我更新的答案以了解一些替代方法。
  • 谢谢你,是的,这减少了一点点。但是,现在我的问题是,压缩时间不是 1-3 分钟,而是大约需要 30 分钟或更长时间。我不知道是否允许将这个问题合并到这个问题中,或者我是否需要创建一个新问题。但是,是否有一种智能、快速的方法来检查索引 3 和 4、时间戳,并将它们存储在字典中,并给十六进制一个数字
  • 即60541567 = 39BCA7F --> 03 9B CA 7F ... 然后给出一个 dict 0: 03 9B CA 7F, 1: next value etc.
  • 我认为您确实应该为实现方面提出一个新问题。
【解决方案2】:

尝试对每个单元格的内容而不是单个字符运行您的算法,然后使用压缩的单元格值创建一个新的 CSV 文件。

如果您提供的数据是较大文件的示例,您可能需要分别对每列运行压缩算法。例如,它可能只有助于压缩第 0,4 和 5 列。

要读取和写入 CSV 文件,请查看 csv 模块,您可以在其中执行以下操作:

import csv
with open('eggs.csv', 'rb') as csvfile:
    spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in spamreader:
        print ', '.join(row)

【讨论】:

  • 谢谢,我没想到。即存储请参阅“6NH8”等重复出现的列。所以你仍然建议在 csv 上写成 dict 但“数字”等于列?
  • 经历了 500 000 行。时间有时会出现 3 次,但很可能会出现一次。与 4 个首字母和数字组合相同。所以这并没有太大帮助:(
【解决方案3】:

对于每一行,在前一行或多行中搜索匹配的子字符串。对于每个匹配的子字符串(例如6NH8,F,A,0,6054156,78.78,2),将匹配的长度和距离发送回副本。这称为 LZ77 压缩。

【讨论】:

  • 谢谢,但我注意到输出 csv 中字典的大小很重要。即 0:["123",345","678"] .. 所以我需要以某种方式最小化我的 dict 中的值的数量?还是我想错了
  • 使用二进制数。不是数字的 ASCII 表示。
  • 是的,我试过了,但是通过我自己的霍夫曼版本,我把所有这些都变成了二进制 [(',', 0), ('5', 1), ('6', 2 ), ('0', 3), ('7', 4), ('8', 5), ('4', 6), ('1', 7), ('9', 8), ('.', 9), ('3', 10), ('\n', 11), ('H', 12), ('F', 13), ('2', 14), ( 'A', 15), ('N', 16), ('B', 17), ('M', 18), ('Q', 19)]。所以我把所有的数字都变成了二进制文件,把“,”换成了0,然后换成了二进制文件,但是文件还是比原来的大
猜你喜欢
  • 1970-01-01
  • 2018-08-29
  • 2021-09-04
  • 2015-01-12
  • 1970-01-01
  • 2021-11-17
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多