Skip to content
Snippets Groups Projects
Commit 8e77ef9d authored by Jonathan Ehret's avatar Jonathan Ehret
Browse files

improve randomization (or not randomization) for non-combined factors

parent 7554bfc3
No related branches found
No related tags found
No related merge requests found
...@@ -39,6 +39,18 @@ TSharedPtr<FJsonObject> USFStudyFactor::GetAsJson() const ...@@ -39,6 +39,18 @@ TSharedPtr<FJsonObject> USFStudyFactor::GetAsJson() const
FSFLoggingUtils::Log("[USFStudyFactor::GetAsJson] unknown MixingOrder!", true); FSFLoggingUtils::Log("[USFStudyFactor::GetAsJson] unknown MixingOrder!", true);
} }
switch (NonCombinedMixingOrder)
{
case ENonCombinedFactorMixingOrder::RandomOrder:
Json->SetStringField("NonCombinedMixingOrder", "RandomOrder");
break;
case ENonCombinedFactorMixingOrder::InOrder:
Json->SetStringField("NonCombinedMixingOrder", "InOrder");
break;
default:
FSFLoggingUtils::Log("[USFStudyFactor::GetAsJson] unknown NonCombinedMixingOrder!", true);
}
switch (Type) switch (Type)
{ {
case EFactorType::Within: case EFactorType::Within:
...@@ -85,6 +97,20 @@ void USFStudyFactor::FromJson(TSharedPtr<FJsonObject> Json) ...@@ -85,6 +97,20 @@ void USFStudyFactor::FromJson(TSharedPtr<FJsonObject> Json)
FSFLoggingUtils::Log("[USFStudyFactor::FromJson] unknown MixingOrder: " + MixingOrderStr, true); FSFLoggingUtils::Log("[USFStudyFactor::FromJson] unknown MixingOrder: " + MixingOrderStr, true);
} }
FString NonCombinedMixingOrderStr = Json->GetStringField("NonCombinedMixingOrder");
if (NonCombinedMixingOrderStr == "RandomOrder")
{
NonCombinedMixingOrder = ENonCombinedFactorMixingOrder::RandomOrder;
}
else if (NonCombinedMixingOrderStr == "InOrder")
{
NonCombinedMixingOrder = ENonCombinedFactorMixingOrder::InOrder;
}
else
{
FSFLoggingUtils::Log("[USFStudyFactor::FromJson] unknown NonCombinedMixingOrder: " + MixingOrderStr, true);
}
FString TypeStr = Json->GetStringField("Type"); FString TypeStr = Json->GetStringField("Type");
if (TypeStr == "Within") if (TypeStr == "Within")
{ {
...@@ -109,10 +135,6 @@ bool USFStudyFactor::CanEditChange(const FProperty * InProperty) const ...@@ -109,10 +135,6 @@ bool USFStudyFactor::CanEditChange(const FProperty * InProperty) const
{ {
return false; return false;
} }
if(InProperty->GetFName()=="Type" && bNonCombined)
{
return false;
}
return true; return true;
} }
...@@ -164,3 +186,25 @@ TArray<int> USFStudyFactor::GenerateLatinSquareOrder(int OrderNr, int NrConditio ...@@ -164,3 +186,25 @@ TArray<int> USFStudyFactor::GenerateLatinSquareOrder(int OrderNr, int NrConditio
return Result; return Result;
} }
TArray<int> USFStudyFactor::GenerateRandomOrder(int OrderNr, int NrConditions)
{
//not the most efficient implementation, but straight forward. Efficiency is not so important when creating study runs.
FRandomStream RNG(OrderNr);
//Create Ordered List;
TArray<int> Conditions;
for (int i = 0; i < NrConditions; ++i)
{
Conditions.Add(i);
}
TArray<int> RandomConditions;
for (int i = 0; i < NrConditions; ++i)
{
int RandomIndex = RNG.RandRange(0, Conditions.Num() - 1);
RandomConditions.Add(Conditions[RandomIndex]);
Conditions.RemoveAt(RandomIndex);
}
check(Conditions.Num()==0)
return RandomConditions;
}
...@@ -204,8 +204,9 @@ TArray<USFCondition*> USFStudyPhase::GenerateConditions(int ParticipantSequenceN ...@@ -204,8 +204,9 @@ TArray<USFCondition*> USFStudyPhase::GenerateConditions(int ParticipantSequenceN
continue; continue;
} }
TArray<int> LatinSquare = USFStudyFactor::GenerateLatinSquareOrder(ParticipantSequenceNr + PhaseIndex, Factor->Levels.Num()); TArray<int> RandomOrder = {};
if (LatinSquare.Num() < ConditionsIndices.Num()) int RandomOrderIndex = 0;
if (Factor->Levels.Num() < ConditionsIndices.Num())
{ {
FSFLoggingUtils::Log( FSFLoggingUtils::Log(
"[USFStudyPhase::GenerateConditions] nonCombined factor levels will be repeated, since factor " + Factor-> "[USFStudyPhase::GenerateConditions] nonCombined factor levels will be repeated, since factor " + Factor->
...@@ -216,8 +217,27 @@ TArray<USFCondition*> USFStudyPhase::GenerateConditions(int ParticipantSequenceN ...@@ -216,8 +217,27 @@ TArray<USFCondition*> USFStudyPhase::GenerateConditions(int ParticipantSequenceN
{ {
check(ConditionsIndices[j][i]==-1); check(ConditionsIndices[j][i]==-1);
//repeat the latin square if it does not provide enough levels if(RandomOrderIndex >= RandomOrder.Num())
ConditionsIndices[j][i] = LatinSquare[j % Factor->Levels.Num()]; {
//generate a new random order (since we maybe have to repeat the levels of this factor and then each repetition should be random
RandomOrder = USFStudyFactor::GenerateRandomOrder(ParticipantSequenceNr + PhaseIndex + i + j, Factor->Levels.Num());
RandomOrderIndex = 0;
}
if(Factor->NonCombinedMixingOrder == ENonCombinedFactorMixingOrder::RandomOrder)
{
ConditionsIndices[j][i] = RandomOrder[RandomOrderIndex++];
}
else if (Factor->NonCombinedMixingOrder == ENonCombinedFactorMixingOrder::InOrder)
{
ConditionsIndices[j][i] = RandomOrderIndex++;
}
else
{
FSFLoggingUtils::Log(
"[USFStudyPhase::GenerateConditions] unknown non-combined mixing order!");
}
} }
} }
......
...@@ -14,6 +14,13 @@ enum class EFactorMixingOrder : uint8 ...@@ -14,6 +14,13 @@ enum class EFactorMixingOrder : uint8
InOrder = 2 UMETA(DisplayName = "In Order: The levels of this factor well be kept in specified order") InOrder = 2 UMETA(DisplayName = "In Order: The levels of this factor well be kept in specified order")
}; };
UENUM()
enum class ENonCombinedFactorMixingOrder : uint8
{
RandomOrder = 0 UMETA(DisplayName = "Random: Fully random using random sequences, seeded by participant running number"),
InOrder = 1 UMETA(DisplayName = "In Order: So Latin-Square randomization of the other factors should lead to equal mapping of conditions to non-combined factor levels")
};
UENUM() UENUM()
enum class EFactorType : uint8 enum class EFactorType : uint8
{ {
...@@ -47,24 +54,25 @@ public: ...@@ -47,24 +54,25 @@ public:
UPROPERTY(BlueprintReadOnly, EditAnywhere) UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<FString> Levels; TArray<FString> Levels;
//how to mix this Factor
UPROPERTY(BlueprintReadOnly, EditAnywhere)
EFactorMixingOrder MixingOrder = EFactorMixingOrder::RandomOrder;
//Whether it is a within or between subjects factor
UPROPERTY(BlueprintReadOnly, EditAnywhere)
EFactorType Type = EFactorType::Within;
/* /*
* bNonCombined means this factor is just used to introduce randomness * bNonCombined means this factor is just used to introduce randomness
* In contrast to normal factors it is not combined with the other factors in * In contrast to normal factors it is not combined with the other factors in
* a n x m x ... design but run in parallel, e.g., to introduce some randomness * a n x m x ... design but run in parallel, e.g., to introduce some randomness
* to repetitions of the same conditions (of the other factors). * to repetitions of the same conditions (of the other factors).
* Currently these are then randomized between all subjects using LatinSquare
* (and repeated in the same order if not enough levels).
*/ */
UPROPERTY(BlueprintReadOnly, EditAnywhere) UPROPERTY(BlueprintReadOnly, EditAnywhere)
bool bNonCombined = false; bool bNonCombined = false;
//how to mix this Factor
UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (EditCondition = "!bNonCombined", EditConditionHides))
EFactorMixingOrder MixingOrder = EFactorMixingOrder::RandomOrder;
UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (EditCondition = "bNonCombined", EditConditionHides))
ENonCombinedFactorMixingOrder NonCombinedMixingOrder = ENonCombinedFactorMixingOrder::RandomOrder;
//Whether it is a within or between subjects factor
UPROPERTY(BlueprintReadOnly, EditAnywhere, meta = (EditCondition = "!bNonCombined", EditConditionHides))
EFactorType Type = EFactorType::Within;
static TArray<int> GenerateLatinSquareOrder(int OrderNr, int NrConditions); static TArray<int> GenerateLatinSquareOrder(int OrderNr, int NrConditions);
static TArray<int> GenerateRandomOrder(int OrderNr, int NrConditions);
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment