重建压缩的数据帧信息

时间:2018-10-13 14:06:02

标签: python pandas matplotlib

我正在使用基于pygame的监视系统收集二进制数据,该系统收集状态数据的分辨率接近但不精确到0.2秒。该对象将处于打开状态(1)或关闭状态(0),并且将进行一小时的监视,最后将收集到18,000个数据点。

我的问题是,学生将使用excel读取此数据集,因此,尽管它可能被认为是一个很小的数据集,但在excel中查看它却简直是压倒性的。我需要此数据集保持易于查看和理解(即:以csv格式时),因此当我说我希望压缩数据帧大小时,我并不是要使用通用的文件压缩算法来减小其文件大小。

大多数情况下,对象的状态将关闭。这意味着从一个时间点到下一个时间点,大概95%的数据点将保持不变。当状态更改为“开”时,“开”状态通常会保留超过0.2秒。

这是一个典型数据框的简短示例,您可以看到,从操作列中,我可以轻松地计算出某些事物保持“打开”(或关闭)状态的总时间,并且matlotlib可以很好地完成工作使用条形图可视化此数据。但是我的问题是,一个真实的数据框每隔0.2秒的时间点(大约)就有大量的行。

我认为仅记录与上一个时间点不同的动作,就可以大大减少数据帧的大小。这确实减小了数据帧的大小,但是现在使数据解释变得复杂。例如,在绘制新数据集时,matplotlib不知道“打开”状态持续多长时间,而我的getOnStatePeriods函数也不知道无法正确测量打开状态的时间跨度。请运行下面的代码,并查看原始数据“ df”,然后查看我尝试使用“ dfSmall”减小数据集大小的方法。

这个数据集不是我认为是时间序列的数据,因为pygame强迫样本在大约0.2秒而不是精确在0.2秒时进行。

我对使用完整数据集测量开/关状态的技术感到满意,在没有任何变化的情况下存储所有时间点事件似乎效率很低。也许我应该使用更好的压缩技术?而且,似乎我被迫使用条形图而不是简单的“图”,因为简单的图给了我对角线过渡...

感谢您的帮助。

import pandas as pd
import numpy as np
import io
import matplotlib.pyplot as plt

try:
    # for Python2
    from cStringIO import StringIO 
except ImportError:
    # for Python3
    from io import StringIO

def getOnStatePeriods(df):
    mask = df['action']==0 #mask is True for specified event
    mask[0] = True # maybe worth setting 1st element in event to zero, or: mask[0] = True
    df.loc[mask,'step1'] = df.loc[mask,'time']
    df['step2'] = df['step1'].fillna(method='ffill')
    df['step3'] = df['time']-df['step2']
    df['step4'] = df['step3'].shift(1)
    df.loc[mask,'step5'] = df.loc[mask,'step4']
    df['step6'] = df['step5'].replace(0, np.nan)
    df['step7'] = df['step6'].shift(-1)
    df.rename(columns={'step7': 'actionTime'}, inplace=True)
    longDf = df # Make one detailed longDf and one concise df:
    df = df[['time','action','actionTime']]
    return df


df = pd.read_csv(StringIO('''
time,action
.203,0
.401,0
.605,1
.802,1
1.001,0
1.201,0
1.403,1
1.606,1
1.803,1
2.004,0
2.201,0
2.407,0
'''.strip()))

dfSmall = pd.read_csv(StringIO('''
time,action
.203,0
.605,1
1.001,0
1.403,1
2.004,0
'''.strip()))

df = getOnStatePeriods(df) #df based on the ORIGINAL large dataframe
dfSmall = getOnStatePeriods(dfSmall) # df containing only times of state changes

fig, axes = plt.subplots(4,1, figsize=(6, 6), sharex=True)
axes[0].set_title("Original df")
axes[0].bar('time','action',data=df, color='red', align='edge', width=0.2)
axes[1].plot('time','action',data=df, color='red', alpha=0.5)
axes[2].set_title("'dfSmall' - where only state changes are recorded.")
axes[2].bar('time','action',data=dfSmall, color='blue', width=0.2)
axes[3].plot('time','action',data=dfSmall, color='blue', alpha=0.5)

plt.tight_layout()
plt.show()

My attempt at compressing a dataframe

1 个答案:

答案 0 :(得分:1)

Run-length encoding(Wikipedia)

import random
import sys

random.seed(42) 


def getValue(lastValue): 
    if random.randint(1,100)==100:  # 1% change chance
        return not lastValue
    return lastValue

data = []
lastValue = False
for _ in range(18000):
    lastValue=getValue(lastValue)
    data.append(lastValue)

print(data)

def runLengthEncoded(data):
    rl = []
    last = data[0]
    occ = 1
    for d in data[1:]:
        if d == last:
            occ += 1
        else:
            rl.append( (last,occ))
            occ = 1
            last = d
    rl.append( (last,occ) )
    return rl

rl = runLengthEncoded(data)

print(rl)

此处输出:

[(False, 110), (True, 90), (False, 297), (True, 173), (False, 37), (True, 108), (False, 28), 
(True, 54), (False, 154), (True, 234), (False, 137), (True, 7), (False, 164), (True, 32), 
(False, 167), (True, 107), (False, 9), (True, 100), (False, 114), (True, 73), (False, 73), 
# snipp # 
(False, 156), (True, 23), (False, 373), (True, 86), (False, 122), (True, 82), (False, 250), 
(True, 75), (False, 207), (True, 102), (False, 42), (True, 14), (False, 359), (True, 324), 
(False, 48), (True, 123), (False, 135), (True, 120), (False, 136), (True, 145), (False, 82)]

True / False是多余的,如果存储初始值,则可以进一步缩短。如果要使用时间戳,只需将戳记存储在值更改的地方。

def runLengthEncoded2(data):
    rl = []
    last = data[0] 
    occ = 1
    for d in data[1:]:
        if d == last:
            occ += 1
        else:
            rl.append(occ)
            occ = 1
            last = d
    rl.append( occ )
    return (data[0],rl)

针对:

(False, [110, 90, 297, 173, 37, 108, 28, 54, 154, 234, 137, 7, 164, 32, 167, 107, 9, 100, 114, 
         73, 73, 10, 21, 71, 35, 74, 238, 13, 20, 382, 112, 213, 67, 331, 13, 25, 74, 100, 48, 
         119, 74, 20, 72, 57, 86, 70, 283, 47, 26, 46, 12, 154, 14, 7, 129, 27, 69, 179, 129, 
         14, 33, 86, 9, 171, 36, 203, 81, 50, 28, 54, 58, 39, 108, 7, 34, 196, 139, 9, 205, 
         15, 45, 21, 209, 22, 40, 39, 19, 305, 15, 351, 24, 212, 3, 37, 26, 7, 150, 106, 176, 
         390, 61, 40, 194, 261, 89, 337, 457, 31, 53, 24, 487, 94, 334, 158, 446, 16, 300, 93, 
         5, 189, 62, 200, 136, 84, 75, 1, 179, 52, 19, 123, 54, 42, 130, 97, 77, 101, 11, 166, 
         85, 126, 156, 23, 373, 86, 122, 82, 250, 75, 207, 102, 42, 14, 359, 324, 48, 123, 135, 
         120, 136, 145, 82])