Генерировать недостающие блоки данных в панде - PullRequest
0 голосов
/ 29 июня 2018

Итак, у меня есть такой фрейм данных:

[5232 rows x 2 columns]
                                   0       2
0                                               
2018-02-01 00:00:00  2018-02-01 00:00:00  435.24
2018-02-01 00:30:00  2018-02-01 00:30:00  357.12
2018-02-01 01:00:00  2018-02-01 01:00:00  301.32
2018-02-01 01:30:00  2018-02-01 01:30:00  256.68
2018-02-01 02:00:00  2018-02-01 02:00:00  245.52
2018-02-01 02:30:00  2018-02-01 02:30:00  223.20
2018-02-01 03:00:00  2018-02-01 03:00:00  212.04
2018-02-01 03:30:00  2018-02-01 03:30:00  212.04
2018-02-01 04:00:00  2018-02-01 04:00:00  212.04
2018-02-01 04:30:00  2018-02-01 04:30:00  212.04
2018-02-01 05:00:00  2018-02-01 05:00:00  223.20
2018-02-01 05:30:00  2018-02-01 05:30:00  234.36

И что я могу сейчас сделать, это заменить часть значений (скажем, 10% случайным образом на NaN:

df_missing.loc[df_missing.sample(frac=0.1, random_state=100).index, 2] = np.NaN

То, что я хотел бы сделать, это сделать то же самое, но со случайными блоками размера x, скажем, 10% данных должны быть заблокированы NaN.

Например, если размер блока равен 4, а доля равна 30%, приведенный выше кадр данных может выглядеть следующим образом:

[5232 rows x 2 columns]
                                   0       2
0                                               
2018-02-01 00:00:00  2018-02-01 00:00:00  435.24
2018-02-01 00:30:00  2018-02-01 00:30:00  357.12
2018-02-01 01:00:00  2018-02-01 01:00:00  NaN
2018-02-01 01:30:00  2018-02-01 01:30:00  NaN
2018-02-01 02:00:00  2018-02-01 02:00:00  NaN
2018-02-01 02:30:00  2018-02-01 02:30:00  NaN
2018-02-01 03:00:00  2018-02-01 03:00:00  212.04
2018-02-01 03:30:00  2018-02-01 03:30:00  212.04
2018-02-01 04:00:00  2018-02-01 04:00:00  212.04
2018-02-01 04:30:00  2018-02-01 04:30:00  212.04
2018-02-01 05:00:00  2018-02-01 05:00:00  223.20
2018-02-01 05:30:00  2018-02-01 05:30:00  234.36

Я понял, что могу получить количество блоков с помощью:

number_of_samples = int((df.shape[0] * proporition) / block_size)

Но я не могу понять, как на самом деле создать недостающие блоки.

Я видел этот вопрос, который полезен, но имеет два предостережения:

  1. Он не изменяет исходный кадр данных со значениями NaN, а только возвращает образцы.
  2. Нет гарантии, что образцы не будут перекрываться (чего в идеале я бы хотел избежать)

Может ли кто-нибудь объяснить, как преобразовать этот ответ для указанных выше пунктов (или объяснить другое решение)?

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

@ caseWestern дал отличное решение, на котором я основал свое собственное:

def block_sample(df_length : int, number_of_samples : int, block_size : int):
    """ Generates the the initial index of a block of block_size WITHOUT replacement.

        Does this by removing x-(block_size+1):x+block_size from the possible values, 
        so that the next value must be at least a block_size away from the last value. 


        Raises
        ------
        ValueError: In cases of more samples than possible.
    """
    full_range = list(range(df_length))
    for _ in range(number_of_samples):
        x = random.sample(full_range, 1)[0]
        indx = full_range.index(x)
        yield x
        del full_range[indx-(block_size-1):indx+block_size]

try: 
    for x in block_sample(df_length, number_of_samples, block_size):
        df_missing.loc[x:x+block_size, 2] = np.NaN
except ValueError:
        pass
0 голосов
/ 29 июня 2018

Этот код выполняет работу довольно не элегантно, используя операторы if для проверки перекрытий в блоках. Он также использует метод chain с распаковкой аргументов (*), чтобы свести список списков в один список:

import pandas as pd
import random
import numpy as np
from itertools import chain

# Example dataframe
df = pd.DataFrame({0: pd.date_range(start = pd.datetime(2018, 2, 1, 0, 0, 0), 
                                    end = pd.datetime(2018, 2, 1, 10, 0, 0), freq = '30 min'),
                   2: np.random.randn(21)})

# Set basic parameters
proportion = 0.4
block_size = 4
number_of_samples = int((df.shape[0] * proportion) / block_size)

# This will hold all indexes to be set to NaN
block_indexes = []

i = 0 

# Iterate until number of samples are found
while i < number_of_samples:

    # Choose a potential start and end
    potential_start = random.sample(list(df.index), 1)[0]
    potential_end = potential_start + block_size

    # Flatten the list of lists
    flattened_indexes = list(chain(*block_indexes))

    # Check to make sure potential start and potential end are not already in the indexes
    if potential_start not in flattened_indexes \
    and potential_end not in flattened_indexes:

        # If they are not, append the block indexes
        block_indexes.append(list(range(potential_start, potential_end)))

        i += 1

# Flatten the list of lists
block_indexes = list(chain(*block_indexes))

# Set the blocks to nan accounting for end of dataframe
df.loc[[x for x in block_indexes if x in df.index], 2] = np.nan

С результатом, примененным к примеру кадра данных:

enter image description here

Я не уверен, как вы хотите обрабатывать блоки в конце информационного кадра, но этот код игнорирует любые индексы, которые находятся за пределами диапазона индекса информационного кадра. Я уверен, что есть более Pythonic способ написать этот код, и любые комментарии будут оценены!

...