Skip to content

Commit

Permalink
Fixed 'std::bad_alloc' error when any observation period start date i…
Browse files Browse the repository at this point in the history
…s before 1970. Fixes #59
  • Loading branch information
Admin_mschuemi authored and Admin_mschuemi committed Jun 20, 2024
1 parent d1ac73c commit 66ce219
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 45 deletions.
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ SelfControlledCaseSeries 5.2.3

Bugfixes

1. Fixing 'ORDER BY is ignored in subqueries without LIMIT' warning when calling `createSccsIntervalData()`.
1. Fixed 'ORDER BY is ignored in subqueries without LIMIT' warning when calling `createSccsIntervalData()`.

2. Fixed 'std::bad_alloc' error when any observation period start date is before 1970.


SelfControlledCaseSeries 5.2.2
Expand Down
4 changes: 1 addition & 3 deletions R/ScriDataConversion.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,13 @@ createScriIntervalData <- function(studyPopulation,
# Ensure all sorted bv caseId:
cases <- studyPopulation$cases[order(studyPopulation$cases$caseId), ]
outcomes <- studyPopulation$outcomes[order(studyPopulation$outcomes$caseId), ]
eras <- sccsData$eras %>%
arrange(.data$caseId)

controlIntervalId <- settings$covariateSettingsList[sapply(settings$covariateSettingsList, function(x) x$isControlInterval)][[1]]$outputIds[1, 1]
data <- Andromeda::andromeda()
convertToSccs(
cases = cases,
outcomes = outcomes,
eras = eras,
eras = sccsData$eras,
includeAge = FALSE,
ageOffset = 0,
ageDesignMatrix = matrix(),
Expand Down
128 changes: 128 additions & 0 deletions src/DateFunctons.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* @file DateFunctions.h
*
* This file is part of SelfControlledCaseSeries
*
* Copyright 2024 Observational Health Data Sciences and Informatics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef DATEFUNCTIONS_H_
#define DATEFUNCTIONS_H_

#include <cmath>

using namespace Rcpp;

namespace ohdsi {
namespace sccs {

// Note: In response to https://github.com/OHDSI/SelfControlledCaseSeries/issues/59
// implementing our own date functions. Only alternatives appear to require
// C++ 20 or the Boost library.

struct Date {
int year;
int month; // 1 - 12
int day; // 1 - 31
};

bool isLeapYear(int year) {
if (year % 4 != 0) return false;
if (year % 100 != 0) return true;
if (year % 400 != 0) return false;
return true;
}

int daysInMonth(int year, int month) {
switch (month) {
case 1: return 31;
case 2: return isLeapYear(year) ? 29 : 28;
case 3: return 31;
case 4: return 30;
case 5: return 31;
case 6: return 30;
case 7: return 31;
case 8: return 31;
case 9: return 30;
case 10: return 31;
case 11: return 30;
case 12: return 31;
default: return 0;
}
}

int daysFromStartOfYear(const Date& date) {
int days = 0;
for (int month = 1; month < date.month; ++month) {
days += daysInMonth(date.year, month);
}
days += date.day;
return days;
}

int differenceInDays(const Date& date1, const Date& date2) {
if (date1.year < date2.year ||
(date1.year == date2.year && date1.month < date2.month) ||
(date1.year == date2.year && date1.month == date2.month && date1.day < date2.day)) {
throw std::invalid_argument("date1 cannot be earlier than date2");
}
int daysDifference = 0;
for (int year = date2.year; year < date1.year; ++year) {
daysDifference += isLeapYear(year) ? 366 : 365;
}
daysDifference -= daysFromStartOfYear(date2);
daysDifference += daysFromStartOfYear(date1);
return std::abs(daysDifference);
}

Date addMonth(const Date& date1) {
Date date2 = date1;
date2.month++;
if (date2.month == 13) {
date2.month = 1;
date2.year++;
}
return date2;
}

Date addDays(const Date& startDate, int daysToAdd) {
if (daysToAdd < 0)
throw std::invalid_argument("daysToAdd cannot be negative");
Date newDate = startDate;

while (daysToAdd > 0) {
int daysThisMonth = daysInMonth(newDate.year, newDate.month);

if (newDate.day + daysToAdd <= daysThisMonth) {
newDate.day += daysToAdd;
daysToAdd = 0;
} else {
daysToAdd -= (daysThisMonth - newDate.day + 1);
newDate.day = 1;
if (newDate.month == 12) {
newDate.month = 1;
newDate.year++;
} else {
newDate.month++;
}
}
}
return newDate;
}

}
}

#endif /* DATEFUNCTIONS_H_ */
51 changes: 13 additions & 38 deletions src/SccsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
#ifndef SCCSCONVERTER_CPP_
#define SCCSCONVERTER_CPP_

#include <ctime>
#include <Rcpp.h>
#include "SccsConverter.h"
#include "PersonDataIterator.h"
#include "NumericIntegration.h"
#include "DateFunctons.h"

using namespace Rcpp;

Expand Down Expand Up @@ -281,42 +281,17 @@ void SccsConverter::computeEventDepObsWeights(std::vector<ConcomitantEra>& conco
}
}

struct tm SccsConverter::addMonth(const struct tm &date) {
struct tm newDate(date);
if (newDate.tm_mon == 11){
newDate.tm_mon = 0;
newDate.tm_year++;
} else {
newDate.tm_mon++;
}
return newDate;
}

int SccsConverter::dateDifference(struct tm &date1, struct tm &date2) {
std::time_t time1 = std::mktime(&date1);
std::time_t time2 = std::mktime(&date2);
int difference = std::difftime(time1, time2) / (60 * 60 * 24);
return difference;
}

void SccsConverter::addMonthEras(std::vector<Era>& eras, const PersonData& personData){
struct tm obsStartDate = {0, 0, 12};
obsStartDate.tm_year = personData.obsStartYear - 1900;
obsStartDate.tm_mon = personData.obsStartMonth - 1;
obsStartDate.tm_mday = personData.obsStartDay;
mktime(&obsStartDate);
struct tm startDate = {0, 0, 12};
startDate.tm_year = personData.obsStartYear - 1900;
startDate.tm_mon = personData.obsStartMonth - 1;
startDate.tm_mday = personData.obsStartDay + personData.startDay;
mktime(&startDate);
struct tm startOfMonth(startDate);
startOfMonth.tm_mday = 1;
struct tm startOfNextMonth = addMonth(startOfMonth);
Date obsStartDate = {personData.obsStartYear, personData.obsStartMonth, personData.obsStartDay};
Date startDate = addDays(obsStartDate, personData.startDay);
Date startOfMonth = startDate;
startOfMonth.day = 1;
Date startOfNextMonth = addMonth(startOfMonth);
int eraStartDay = personData.startDay;
int nextEraStartDay = std::min(dateDifference(startOfNextMonth, obsStartDate), personData.endDay + 1);
int month = startOfMonth.tm_mon;
int nextEraStartDay = std::min(differenceInDays(startOfNextMonth, obsStartDate), personData.endDay + 1);
int month = startOfMonth.month;
while (eraStartDay <= personData.endDay) {
// std::cout << "eraStartDay: " << eraStartDay << ", startOfMonth.year: " << startOfMonth.year << ", startOfMonth.month: " << startOfMonth.month << "\n";
if (includeAge){
int ageIndex = personData.ageInDays + eraStartDay - ageOffset;
if (ageIndex < 0) {
Expand All @@ -331,12 +306,12 @@ void SccsConverter::addMonthEras(std::vector<Era>& eras, const PersonData& perso
}
if (includeSeason){
for (int i = 0; i < seasonDesignMatrix.ncol(); i++){
Era era(eraStartDay, nextEraStartDay - 1, seasonIdOffset + i, seasonDesignMatrix(month, i));
Era era(eraStartDay, nextEraStartDay - 1, seasonIdOffset + i, seasonDesignMatrix(month - 1, i));
eras.push_back(era);
}
}
if (includeCalendarTime){
int monthIndex = (startOfMonth.tm_year + 1900) * 12 + startOfMonth.tm_mon - calendarTimeOffset;
int monthIndex = (startOfMonth.year + 1900) * 12 + startOfMonth.month - 1 - calendarTimeOffset;
if (monthIndex < 0) {
monthIndex = 0;
} else if (monthIndex >= calendarTimeDesignMatrix.nrow()) {
Expand All @@ -349,9 +324,9 @@ void SccsConverter::addMonthEras(std::vector<Era>& eras, const PersonData& perso
}
eraStartDay = nextEraStartDay;
startOfMonth = startOfNextMonth;
month = startOfMonth.tm_mon;
month = startOfMonth.month;
startOfNextMonth = addMonth(startOfMonth);
nextEraStartDay = std::min(dateDifference(startOfNextMonth, obsStartDate), personData.endDay + 1);
nextEraStartDay = std::min(differenceInDays(startOfNextMonth, obsStartDate), personData.endDay + 1);
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/SccsConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,6 @@ class SccsConverter {
void addToResult(const ConcomitantEra& era, int outcomeCount, const double duration, const int64_t& observationPeriodId);
void removeNonRiskOrControlIntervals(std::vector<ConcomitantEra>& concomitantEras);
void computeEventDepObsWeights(std::vector<ConcomitantEra>& concomitantEras, const PersonData& personData);
int dateDifference(struct tm &date1, struct tm &date2);
struct tm addMonth(const struct tm &date);
void addMonthEras(std::vector<Era>& eras, const PersonData& personData);
void addCovariateEra(std::vector<Era>& outputEras, int start, int end, int leftCensor, int rightCensor, int covariateIdRow, const CovariateSettings& covariateSettings);
void addCovariateEras(std::vector<Era>& outputEras, const std::vector<Era>& eras, const CovariateSettings covariateSettings);
Expand Down
3 changes: 2 additions & 1 deletion tests/testthat/test-eunomia.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ test_that("Running multiple analyses against Eunomia", {
)
)

getDbSccsDataArgs <- createGetDbSccsDataArgs(deleteCovariatesSmallCount = 1)
getDbSccsDataArgs <- createGetDbSccsDataArgs(deleteCovariatesSmallCount = 1,
studyStartDates = "20000101")

createStudyPopulationArgs1 <- createCreateStudyPopulationArgs(
naivePeriod = 180,
Expand Down

0 comments on commit 66ce219

Please sign in to comment.