회귀 : 커머스 제품 판매량 예측 3
※ 모델 고도화
데이터 전처리의 결과를 활용하여, 온라인 판매량 예측을 위한 모델링 과정을 배웁니다. 모델링은 인공지능 프로젝트에서 중심적인 단계로, 이를 통해 우리는 데이터로부터 패턴을 학습하고 예측 작업을 수행할 수 있는 모델을 구축합니다. 특히, 각 온라인 채널의 판매량을 예측하는 데 필요한 모델을 훈련시키는 방법을 배웁니다. 모델을 효과적으로 구축하고 최적화하는 능력은 효율적인 재고 관리와 타겟 마케팅 전략 수립을 위한 중요한 요소입니다.
1. Configuration
def initialize_configuration(seed):
# 모델 구성 및 학습 설정 초기화
CFG = {
'TRAIN_WINDOW_SIZE': 28,
'PREDICT_SIZE': 21,
'EPOCHS': 8,
'LEARNING_RATE': 0.001,
'BATCH_SIZE': 256,
'SEED': seed
}
return CFG
device = torch.device('cuda:0') if torch.cuda.is_available() else torch.device('cpu')
이 단계에서는 모델 학습과 예측 과정에 사용될 설정 값을 정의하는 과정을 다룹니다. initialize_oonfiguration 함수는 다양한 설정 값들을 포함하는 딕셔너리 CFO를 생성하고 반환합니다. 이 설정 값들은 모델의 학습 방식, 데이터 처리 방식 등을 결정하는 데 사용됩니다. 각 설정 값의 역할은 다음과 같습니다.
함수 정의 (initialize_oonfiguration):
seed 함수는 난수 생성 시 사용될 기본 시드 값을 매개변수로 받습니다. 이 시드 값은 모델의 학습과 데이터 처리 과정의 재현성을 보장하는 데 사용됩니다.
CFG 딕셔너리:
- TRAIN_WINDOWNL_SIZE 모델이 한 번에 학습할 데이터의 시간 창 크기입니다. 이 값은 모델이 과거 데이터를 얼마나 멀리까지 고려할지 결정합니다.
- PREDICT_SIZE 모델이 한 번의 실행으로 예측할 미래 기간의 크기입니다. 이는 예측의 범위를 결정합니다.
- EPOCHS. 학습 과정에서 전체 데이터셋을 몇 번 반복하여 사용할지 결정하는 값입니다. 높은 값은 모델의 성능을 향상시킬 수 있지만, 과적합(overfitting)의 위험도 증가시킬 수 있습니다.
- LEARNING_RATE 학습률은 모델이 학습하는 속도를 조절합니다. 적절한 학습률은 모델이 최적의 솔루션으로 수렴하는 데 중요한 역할을 합니다.
- BATCH_SIZE 한 번의 학습 반복(iteration)에서 모델이 처리할 데이터의 개수입니다. 배치 크기는 학습 속도와 메모리 사용량에 직접적인 영향을 미칩니다.
- SEED 난수 생성 시 사용될 기본 시드 값입니다. 이는 실험의 재현성을 위해 설정됩니다.
함수의 반환:
설정된 CFe 딕셔너리를 반환합니다. 이 딕셔너리는 모델의 학습 및 예측 과정에서 필요한 다양한 설정 값을 포함합니다.
TRAIN_WINDOW_SIZE'를 28로 설정한 이유는 한 달이라는 기간의 데이터로 학습시켜 절성 변동, 프로모션 효과, 그리고 고객 구매 패턴에 대한 정보를 주기 위함이었습니다.
그리고 'LEARNING_RATE'는 높은 학습률은 모델이 최적해를 지나쳐 발산할 위험이 있고, 낮은 학습률은 학습 속도를 늦추어 시간이 많이 소요됩니다.
0.001의 학습률은 이러한 극단적인 경우를 피하면서 효과적인 학습 결과를 달성하기에 적절한 중간값으로 생각해서 보통 0.001로 설정해서 진행하는 편입니다.
설정 초기화 및 디바이스 설정:
각 시드 값에 대해 설정을 초기화하고, PyTorch 모델을 학습시킬 디바이스(CPU 또는 GPU)를 설정합니다.
2. preprocess_data
이전에 정의함수 calculate_median_of_non_zeros, fill_zeros_before_first_non_zero_with_median, fill_zeros_after_last_non_zero_with_median, interpolate_zeros_between_non_zeros와 중앙값(median_value) 를 이용하여 데이터를 보간합니다.
def preprocess_data(train_data, CFG):
ids = train_data['ID']
date_columns = train_data.columns[7:]
grouped_by_product = train_data.groupby('제품')[date_columns].sum().reset_index()
merged_df = pd.merge(train_data.drop(columns=date_columns), grouped_by_product, on='제품', how='left')
train_data = merged_df.copy()
# 0값 처리 및 판매 데이터 보간
for i in range(len(train_data)):
value = np.array(train_data.iloc[i][7:])
median_value = calculate_median_of_non_zeros(value)
value = fill_zeros_before_first_non_zero_with_median(value, median_value)
value = fill_zeros_after_last_non_zero_with_median(value, median_value)
interpolated_sales_data = interpolate_zeros_between_non_zeros(value)
train_data.iloc[i, 7:] = interpolated_sales_data
return train_data, ids
3. train & test 데이터 생성
def make_train_data(data, train_size, predict_size):
num_rows = len(data)
num_cols = len(data.columns) - 8
window_size = train_size + predict_size
input_data = np.empty((num_rows * (num_cols - window_size + 1), train_size, 1))
target_data = np.empty((num_rows * (num_cols - window_size + 1), predict_size))
idx = 0
for i in range(num_rows):
sales_data = np.array(data.iloc[i, 8:])
for j in range(num_cols - window_size + 1):
window = sales_data[j : j + window_size]
input_data[idx] = window[:train_size].reshape(-1, 1)
target_data[idx] = window[train_size:]
idx += 1
return input_data, target_data
def make_predict_data(data, train_size):
num_rows = len(data)
input_data = np.empty((num_rows, train_size, 1))
for i in range(num_rows):
sales_data = np.array(data.iloc[i, -train_size:])
input_data[i] = sales_data.reshape(-1, 1)
return input_data
class CustomDataset(Dataset):
def __init__(self, X, Y):
self.X = X
self.Y = Y
def __getitem__(self, index):
if self.Y is not None:
return torch.Tensor(self.X[index]), torch.Tensor(self.Y[index])
return torch.Tensor(self.X[index])
def __len__(self):
return len(self.X)
이 단계는 시계열 데이터를 활용하여 훈련 및 테스트 데이터셋을 생성하는 과정입니다.
make_train_data 함수와 make_prediot_data 함수, 그리고 CustomDataset 클래스를 사용하여 이 작업을 수행합니다.
make_train_data 함수:
이 함수는 훈련 데이터와 타겟 데이터를 생성합니다. train_size는 입력 데이터로 사용될 과거의 시간 단위 수를, prediot_size는 예측할 미래의 시간 단위 수를 의미합니다.
함수는 먼저 전체 데이터에서 날짜 컬럼만을 선택하여, 각 제품별 판매량의 시계열 데이터를 생성합니다. 이때, window_size는 입력 데이터와 예측할 데이터를 포함한 전체 창의 크기입니다.
입력 데이터와 타겟 데이터는 각각 input_data 배열과 target_data 배열에 저장됩니다. 이 배열들은 초기화 과정에서 적절한 크기로 설정됩니다.
데이터의 각 행을 순회하며, 지정된 train_size에 따라 과거 데이터를 입력으로, prediot_aize에 따라 미래 데이터를 타겟으로 설정하여 배열을 채웁니다.
make_prediot_data 함수:
이 함수는 예측을 위한 입력 데이터를 생성합니다. 특히, 테스트 데이터셋을 만드는 데 사용됩니다.
입력 데이터는 각 행(제품)의 최근 train_size만큼의 판매 데이터를 사용하여 초기화됩니다. 이 데이터는 모델에 입력되어 미래 판매량의 예측에 사용됩니다.
CustomDataset 클래스:
PyTorch의 Dataset 클래스를 상속받아 정의된 사용자 정의 데이터셋 클래스입니다. 이 클래스는 모델 학습에 필요한 입력 데이터(x)와 타겟 데이터(Y)를 관리합니다.
_getitem_메소드는 데이터 로더로부터 요청된 인덱스에 해당하는 데이터 샘플을 반환합니다. 예측 모드에서는 타겟 데이터 없이 입력 데이터만 반환됩니다.
_len 메소드는 데이터셋의 전체 길이를 반환합니다.
이 과정을 통해 생성된 훈련 및 테스트 데이터셋은 시계열 예측 모델의 학습 및 평가에 사용됩니다.
4. 모델 정의
class BaseModel(nn.Module):
#초기화 메서드
def __init__(self, CFG):
super(BaseModel, self).__init__()
self.hidden_size = 512
self.lstm = nn.LSTM(1, self.hidden_size, batch_first=True, bidirectional=True)
self.fc = nn.Sequential(
nn.Linear(self.hidden_size * 2, self.hidden_size // 2),
nn.ReLU(),
nn.Dropout(),
nn.Linear(self.hidden_size // 2, CFG['PREDICT_SIZE'])
)
self.actv = nn.ReLU()
#순전파 메서드
def forward(self, x):
batch_size = x.size(0)
hidden = self.init_hidden(batch_size, x.device)
lstm_out, hidden = self.lstm(x, hidden)
last_output = lstm_out[:, -1, :]
output = self.actv(self.fc(last_output))
return output.squeeze(1)
#초기 hidden state 초기화
def init_hidden(self, batch_size, device):
return (torch.zeros(2, batch_size, self.hidden_size, device=device),
torch.zeros(2, batch_size, self.hidden_size, device=device))
모델 클래스 정의:
Bascillodel 클래스는 PyTorch의 nn.Module을 상속받아 정의됩니다. 이 클래스 내에서 신경망의 구조와 순전파(forward pass) 로직을 정의합니다.
초기화 메서드 (_init_):
CFe 매개변수를 통해 모델 설정을 전달받습니다. 이 설정에는 예측할 출력의 크기 등이 포함될 수 있습니다.
self.hidden_size는 LSTM 층의 hidden state 크기를 정의합니다. 여기서는 512로 설정되어 있습니다.
aclf.late는 양방향 Long Short-Term Memory(LSTM) 층을 정의합니다. LSTM은 시퀀스 데이터 처리에 적합한 신경망 구조로, 여기서는 입력 차원이 1(하나의 특성을 가진 시퀀스)이고, hidden state의 크기가 512입니다. batch_first=True 설정은 입력 데이터의 배치 크기가 텐서의 첫 번째 차원으로 들어옴을 명시합니다. 양방향 LSTM은 시간의 순방향과 역방향 양쪽으로 데이터를 처리하여, 각 시점에서 의 정보를 더욱 풍부하게 반영할 수 있습니다.
self.fc 완전 연결 층(Fully Connected Layers)을 정의합니다. 완전 연결 층은 모델의 출력을 최종적으로 생성합니다. 양방향 LSTM 의 결과는 hidden_size의 두 배(양쪽 방향에서 각각 hidden_size)의 크기를 갖습니다. 여기서 첫 번째 선형(Linear) 층은 이 크기를 hidden_size//2로 줄이고, 활성화 함수(ReLU)와 드롭아웃을 적용한 후, 두 번째 선형 층을 통해 최종 출력 크기인 CFG['PREDICT_SIZE] 로 매핑합니다. 이 구조는 네트워크가 복잡한 패턴을 학습할 수 있도록 도와줍니다.
self.aotv는 활성화 함수로서 nn.ReLU()를 사용하여 정의됩니다. 이 함수는 네트워크의 비선형 학습 능력을 향상시키며, 출력 데이터의 최종 처리에 사용됩니다.
순전파 메서드 (forward):
입력 데이터 처리: 입력 데이터 x에 대한 모델의 순전파 단계를 정의합니다. 이 메서드는 입력 데이터 X가 우선 LSTM 층을 통과하면서 처 리되는 과정을 설명합니다.
batoh_size는 입력 데이터의 배치 크기를 나타내며, x_aize(0)을 통해 추출됩니다. 이는 네트워크에 데이터를 어떻게 공급할지 결정하는 데 중요합니다.
hidden LSTM의 양방향 처리를 위해 초기 hidden state와 cell state를 생성합니다. 이는 self.init_hidden(batch_size. x.device) 메서드를 통해 각 배치의 처리 시작에 맞춰 초기화됩니다. 각 state는 LSTM이 양방향이므로 2배의 크기를 가집니다.
LSTM 처리 및 출력 추출: Istn_out. hidden = self.lstm(x, hidden)에서 LSTM 처리가 수행되며, 이 과정에서 네트워크는 시퀀스 데이터 를 순방향과 역방향 모두에서 처리합니다. 처리된 데이터 중 last_output = Istm_out[:, -1. :]을 통해 각 시퀀스의 마지막 출력만을 추 출합니다. 이는 시퀀스 처리의 최종 상태를 나타내며 다음 계층의 입력으로 사용됩니다.
완전 연결 총 처리 및 최종 출력 계산: output = self.aotv(self.fo(last_output))에서 완전 연결 층을 거쳐 활성화 함수 ReLU를 적용합니다. 이 단계는 모델이 복잡한 데이터 패턴을 학습하고, 이를 바탕으로 최종 예측값을 생성합니다. return output.squeeze(1)은 최종 결 과를 적절한 형태로 조정하여 반환합니다.
초기 hidden state 초기화 (init_hidden):
모델 학습을 시작할 때 사용할 hidden state와 cell state를 0으로 초기화하는 메서드입니다. 양방향 LSTM을 고려하여 각 state의 첫 번째 차원은 2입니다.
LSTM 층의 hidden state와 cell state를 초기화합니다. 이 초기화는 학습을 시작하기 전에 수행되며, 모든 값은 0으로 설정됩니다. 이는 모델이 각 배치를 독립적으로 처리할 수 있도록 하여, 이전 배치의 학습 결과가 다음 배치에 영향을 주지 않게 합니다.
bi-LSTM 사용 이유
Bi-LSTM 모델을 사용한 이유는, 단방향 LSTM 모델과 비교했을 때, 이 모델이 과거 데이터와 미래 데이터를 동시에 분석할 수 있기 때문 입니다. 단방향 LSTM은 주로 과거 정보만을 사용하여 미래를 예측하는 반면, Bi-LSTM은 과거와 미래 양쪽 방향의 데이터를 모두 고려하여 패턴과 트렌드를 더 정확하게 파악할 수 있습니다.
5. 모델 학습
def train_model(model, train_loader, CFG, device):
# 손실 함수와 옵티마이저 설정
criterion = nn.MSELoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=CFG['LEARNING_RATE'])
model.to(device)
# 에포크 수만큼 학습을 반복
for epoch in range(CFG['EPOCHS']):
model.train()
train_loss = []
for inputs, targets in tqdm(train_loader, desc=f"Epoch {epoch+1}/{CFG['EPOCHS']}"):
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
train_loss.append(loss.item())
avg_loss = np.mean(train_loss)
print(f'Epoch [{epoch+1}/{CFG["EPOCHS"]}], Train Loss: {avg_loss:.4f}')
return model
이 단계는 모델 학습 단계입니다.
모델 학습 함수 정의:
train_model 함수는 학습을 위한 주요 요소를 포함합니다. 이 함수는 모델, 학습 데이터 로더, 설정 사전(CFG), 그리고 사용할 디바이스를 인자로 받습니다.
손실 함수와 옵티마이저 설정:
oriterion 평균제곱오차 손실(Mean Squared Error Loss)을 사용하여 모델의 예측과 실제 타겟 간의 차이를 계산합니다. 이 손실 함수는 회귀 문제에 적합 합니다.
optimizer: Adam 옵티마이저를 사용하여 모델의 매개변수를 업데이트합니다. 학습률(LEARNING_RATE)은 CFG 사전을 통해 설정됩니다. 이때 Adam Optimizer 를 선택한 이유는 적응형 학습률 조정 능력 때문입니다. 판매량 데이터의 변동성을 고려해 각 매개변수의 학습률을 개별적으로 조정함으로써, 빠른 수렴을 도모하고 지역 최소값에 빠지는 문제를 줄일 수 있다 생각하였습니다. 일반적으로 사용되는 다른 옵티마이저인 SGD와 비교할 때, Adam은 데이터의 복잡한 패 턴을 더 효과적으로 학습하며, 경사 하강의 방향성을 안정적으로 유지합니다. 이러한 특성은 판매량 예측과 같이 정확도가 중요한 경우에 특히 유리하다 판단하여 사용하였습니다.
모델 및 데이터 준비:
모델과 데이터는 지정된 디바이스(예: CPU 또는 GPU)로 이동됩니다. 이는 계산 효율성을 높이고, 가능한 경우 병렬 처리를 활용하기 위함입니다.
학습 과정:
학습은 설정된 에포크 수(EPOCHS)만큼 반복됩니다. 각 에포크에서는 다음 단계를 수행합니다:
모델을 학습 모드로 설정합니다.
tqdm을 사용해 학습 진행 상황을 시각적으로 표시합니다.
각 배치에 대해 입력 데이터와 타겟을 모델에 전달하고, 손실을 계산한 후, 그 손실을 사용해 모델 매개변수의 그래디언트를 업데이트합니다.
optimizer.zero_grad():그래디언트를 초기화합니다. 이는 매 학습 단계에서 누적되는 그래디언트를 방지합니다.
loss oriterion(outputs, targets) : 모델의 출력과 실제 목표값 사이의 손실을 계산합니다.
Ioaa.baooward(): 손실에 대한 그래디언트를 계산합니다. 이는 모델의 매개변수를 업데이트하는 데 필요한 기울기를 제공합니다.
optimizer.step(): 계산된 그래디언트를 사용하여 모델의 매개변수를 업데이트합니다.
손실은 train_loss 리스트에 저장되고, 에포크가 끝날 때 평균 손실이 계산되어 출력됩니다.
함수의 반환:
학습이 완료된 모델이 반환됩니다. 이 모델은 학습된 매개변수를 포함하며, 평가 단계나 실제 예측에 사용될 수 있습니다.
모델 학습은 모델이 데이터에서 패턴을 학습하고, 이를 바탕으로 예측을 수행할 수 있게 하는 핵심 단계입니다. 학습 과정을 통해 얻은 손실 값의 변화는 모델이 학습하는 과정에서 얼마나 성능이 개선되고 있는지를 평가하는 데 중요한 지표가 됩니다.
6. 예측
def inference(model, test_loader, device):
predictions = []
with torch.no_grad():
for X in tqdm(iter(test_loader), desc="Inference"):
X = X.to(device)
output = model(X)
output = output.cpu().numpy()
predictions.extend(output)
return np.array(predictions)
이 단계는 학습된 모델을 사용하여 테스트 데이터셋에 대한 예측을 수행하는 과정입니다.
예측 함수 정의:
inferenoe 함수는 예측을 위한 모델, 테스트 데이터 로더, 그리고 계산을 수행할 디바이스를 인자로 받습니다.
예측값 저장:
함수 내에서 예측값을 저장할 빈 리스트 prediotiona를 초기화합니다. 이 리스트는 모든 배치에 대한 예측값을 수집하는 데 사용됩니다.
그라디언트 비활성화:
torch.no_grad() 컨텍스트 매니저를 사용하여 예측 시 그라디언트 계산을 비활성화합니다. 이는 메모리 사용량을 줄이고, 예측 수행 속도를 향상시키는 데 도움이 됩니다.
배치 단위 처리:
테스트 데이터셋을 teat_loader를 통해 배치 단위로 순회합니다. 각 배치에 대해, 데이터를 디바이스로 이동시킨 후 모델을 통해 예측을 수행합니다.
데이터를 디바이스로 이동:
입력 데이터 X는 .to(devior) 메소드를 사용하여 계산이 수행될 디바이스(CPU 또는 GPU)로 이동됩니다. 이 단계는 데이터를 모델이 있는 같은 디바이스로 전송하 여 추론 속도를 최적화합니다.
모델로부터 예측값 계산:
model(X) 호출은 현재 배치의 데이터를 모델에 전달하여 예측을 수행합니다. 이 과정에서 모델은 학습된 가중치를 사용하여 입력 데이터에 대한 출력값(예측값)을 계산합니다.
예측값 처리 및 저장:
모델의 출력값은 opu().numpy()를 통해 디바이스에서 CPU로 이동되고, NumPy 배열로 변환됩니다. 이는 후처리 작업이나 결과 분석을 용이하게 합니다. 변환된 예측값은 predictions 리스트에 추가됩니다.
함수의 반환:
함수는 모든 배치에 대해 수집된 예측값을 포함하는 NumPy 배열을 반환합니다. 이 배열은 모델의 예측 성능을 평가하는 데 사용될 수 있습니다.
7. 전체 프로세스 실행
def run_full_process(seed_list, shopping_mall_ids, submission_template_path, submission_save_path=None):
submit_template = pd.read_csv(submission_template_path)
data_path_format = 'train_shopping_{}.csv'
for seed in seed_list:
CFG = initialize_configuration(seed)
final_submission = submit_template.copy()
device = "cuda" if torch.cuda.is_available() else "cpu"
for mall_id in shopping_mall_ids:
data_path = data_path_format.format(mall_id)
train_data = pd.read_csv(data_path)
train = train_data[:100]
train_data, ids = preprocess_data(train, CFG)
train_input, train_target = make_train_data(train_data, CFG['TRAIN_WINDOW_SIZE'], CFG['PREDICT_SIZE'])
test_input = make_predict_data(train_data, CFG['TRAIN_WINDOW_SIZE'])
train_loader = DataLoader(CustomDataset(train_input, train_target), batch_size=CFG['BATCH_SIZE'], shuffle=True)
test_loader = DataLoader(CustomDataset(test_input, None), batch_size=CFG['BATCH_SIZE'], shuffle=False)
model = BaseModel(CFG).to(device)
trained_model = train_model(model, train_loader, CFG, device)
predictions = inference(trained_model, test_loader, device)
pred_cols = final_submission.columns[1:] # 'ID' 제외한 칼럼 ('2023-04-25'부터 마지막까지)
for i, idx in enumerate(ids):
if idx in final_submission['ID'].values:
final_submission.loc[final_submission['ID'] == idx, pred_cols] = predictions[i]
submission_filename = f'submission_seed_{seed}.csv'
final_submission.to_csv(submission_filename, index=False)
seed_list = [42, 21]
shopping_mall_ids = ['S001-00001', 'S001-00002', 'S001-00003', 'S001-00004', 'S001-00005', 'S001-00006', 'S001-00007', 'S001-00008', 'S001-00009', 'S001-00010']
submission_template_path = 'sample_submission.csv'
run_full_process(seed_list, shopping_mall_ids, submission_template_path)
df = pd.read_csv('submission_seed_42.csv')
print(df.head(10))
이 단계에서는 데이터 로드부터 모델 학습, 예측, 결과 처리, 제출 파일 생성에 이르는 전체 프로세스를 실행하는 함수를 정의합니다.
전체 프로세스 실행 함수 (run_full_process):
seed_list: 실험의 재현성을 위한 시드 값들의 리스트입니다.
shopping_mall_ids: 원본 학습 데이터에서 쇼핑몰 칼럼(column)별로 분리하여 데이터를 저장하였습니다.
submission_template_path: 제출을 위한 템플릿 파일의 경로입니다.
데이터셋 로드:
pd.read_oav를 사용하여 전체 학습 데이터셋과 제출 템플릿을 로드합니다.
쇼핑몰별 데이터 전처리 및 모델링:
전처리 함수 preprocess_data를 통해 특정 쇼핑몰에 대한 데이터를 필터링하고 전처리합니다.
make_train_data와 make_prediot_data 함수를 사용하여 훈련 및 테스트 데이터를 생성합니다.
DataLoader를 생성하여 모델 학습 및 예측에 사용할 데이터의 배치 관리를 합니다.
모델 학습 및 예측:
Bascillodel 클래스로 모델을 정의하고, train_model 함수로 모델을 학습합니다.
학습된 모델을 inference 함수에 전달하여 테스트 데이터셋에 대한 예측을 수행합니다.
결과 처리 및 제출 파일 생성:
ID에 해당하는 행의 값을 예측값으로 채웁니다.
제출 파일 저장:
시드 값에 따른 최종 제출 파일을 지정된 경로에 저장합니다. 파일 이름에는 사용된 시드 값을 포함하여, 다양한 실험 결과를 구분할 수 있게 합니다.
ID 2023-04-25 2023-04-26 2023-04-27 \
0 SAMPLE_00000 5.740875720977783 7.0803117752075195 7.184045791625977
1 SAMPLE_00001 2.116002082824707 3.1464099884033203 3.5221810340881348
2 SAMPLE_00002 5.131818771362305 6.107377529144287 5.967116355895996
3 SAMPLE_00003 9.02763843536377 10.556933403015137 11.067365646362305
4 SAMPLE_00004 0.0 0.0 0.0
5 SAMPLE_00005 9.315110206604004 11.013511657714844 11.077516555786133
6 SAMPLE_00006 5.87868595123291 7.509851455688477 7.7476654052734375
7 SAMPLE_00007 117.98759460449219 119.99737548828125 125.90158081054688
8 SAMPLE_00008 121.43714904785156 146.9545135498047 127.71138763427734
9 SAMPLE_00009 0.0 0.0 0.0
2023-04-28 2023-04-29 2023-04-30 \
0 8.494733810424805 9.340414047241211 8.502113342285156
1 4.433935165405273 4.826242446899414 4.644850730895996
2 6.601658344268799 6.687892436981201 6.844604969024658
3 12.306112289428711 12.745161056518555 12.79222297668457
4 0.0 0.0 0.0
5 12.209394454956055 12.687677383422852 12.15878677368164
6 8.983718872070312 8.708133697509766 8.501056671142578
7 122.54263305664062 128.1316375732422 122.37117004394531
8 131.65225219726562 139.91619873046875 112.94149780273438
9 0.0 0.0 0.0
2023-05-01 2023-05-02 2023-05-03 ... \
0 8.65164566040039 9.664709091186523 9.725944519042969 ...
1 4.66138219833374 5.6435136795043945 5.282917499542236 ...
2 6.834993362426758 7.236598014831543 6.8054914474487305 ...
3 12.563640594482422 14.002625465393066 13.417074203491211 ...
4 0.0 0.0 0.0 ...
5 12.599699020385742 12.577821731567383 12.351282119750977 ...
6 9.68342399597168 9.588772773742676 9.084351539611816 ...
7 125.66258239746094 127.291015625 122.17330932617188 ...
8 108.66127014160156 115.80770874023438 109.01517486572266 ...
9 0.0 0.0 0.0 ...
2023-05-06 2023-05-07 2023-05-08 \
0 8.785821914672852 8.67233657836914 8.596297264099121
1 5.426047325134277 5.1559953689575195 4.770583629608154
2 7.017678260803223 6.23081111907959 6.914112091064453
3 12.881349563598633 13.409868240356445 12.735153198242188
4 0.0 0.0 0.0
5 12.630165100097656 11.71416187286377 11.823201179504395
6 9.18580436706543 8.59533977508545 7.971094131469727
7 131.05970764160156 123.01661682128906 134.4537353515625
8 99.8651123046875 93.00877380371094 87.27902221679688
9 0.0 0.0 0.0
2023-05-09 2023-05-10 2023-05-11 \
0 9.501180648803711 9.570451736450195 8.531335830688477
1 5.485651016235352 5.6760969161987305 5.866710662841797
2 7.462430953979492 7.658902168273926 7.914555072784424
3 13.674924850463867 13.347535133361816 13.59689998626709
4 0.0 0.0 0.0
5 12.838356018066406 13.007694244384766 13.010241508483887
6 8.507349014282227 9.303351402282715 9.039002418518066
7 136.68185424804688 132.43353271484375 127.79161834716797
8 85.46113586425781 83.32523345947266 80.0481948852539
9 0.0 0.0 0.0
2023-05-12 2023-05-13 2023-05-14 \
0 8.141727447509766 8.736347198486328 7.809353351593018
1 6.171550750732422 6.097405433654785 5.157583713531494
2 7.566132545471191 6.573257923126221 6.814200401306152
3 12.99095344543457 13.528559684753418 12.858115196228027
4 0.0 0.0 0.0
5 14.193697929382324 13.319413185119629 12.744568824768066
6 9.13436508178711 9.756704330444336 9.158963203430176
7 121.97761535644531 124.54107666015625 111.17593383789062
8 90.70258331298828 69.2987060546875 86.3807601928711
9 0.0 0.0 0.0
2023-05-15
0 8.376204490661621
1 5.2018513679504395
2 7.580348968505859
3 13.215149879455566
4 0.0
5 11.855440139770508
6 9.46666145324707
7 122.66866302490234
8 84.12775421142578
9 0.0
[10 rows x 22 columns]