@iiasa/ixmp4-ts 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cjs/core/exceptions.js +2 -2
  2. package/dist/cjs/core/iamc/data.js +56 -34
  3. package/dist/cjs/core/iamc/variable.js +10 -0
  4. package/dist/cjs/core/meta.js +15 -5
  5. package/dist/cjs/core/model.js +10 -0
  6. package/dist/cjs/core/region.js +10 -0
  7. package/dist/cjs/core/run.js +14 -4
  8. package/dist/cjs/core/scenario.js +10 -0
  9. package/dist/cjs/core/unit.js +10 -0
  10. package/dist/cjs/core/utils.js +187 -55
  11. package/dist/cjs/data/base.js +25 -7
  12. package/dist/cjs/data/iamc/datapoint.js +18 -11
  13. package/dist/cjs/data/iamc/timeseries.js +12 -12
  14. package/dist/cjs/data/iamc/variable.js +9 -4
  15. package/dist/cjs/data/meta.js +9 -4
  16. package/dist/cjs/data/model.js +9 -4
  17. package/dist/cjs/data/region.js +9 -4
  18. package/dist/cjs/data/run.js +9 -4
  19. package/dist/cjs/data/scenario.js +9 -4
  20. package/dist/cjs/data/unit.js +9 -4
  21. package/dist/cjs/data/utils.js +4 -5
  22. package/dist/cjs/dataframe/dataframe.js +1 -5
  23. package/dist/cjs/dataframe/utils.js +4 -5
  24. package/dist/cjs/index.js +3 -1
  25. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  26. package/dist/esm/core/iamc/data.js +50 -30
  27. package/dist/esm/core/iamc/variable.js +8 -0
  28. package/dist/esm/core/meta.js +8 -0
  29. package/dist/esm/core/model.js +8 -0
  30. package/dist/esm/core/region.js +8 -0
  31. package/dist/esm/core/run.js +8 -0
  32. package/dist/esm/core/scenario.js +8 -0
  33. package/dist/esm/core/unit.js +8 -0
  34. package/dist/esm/core/utils.js +176 -50
  35. package/dist/esm/data/base.js +16 -0
  36. package/dist/esm/data/iamc/datapoint.js +5 -0
  37. package/dist/esm/data/iamc/variable.js +3 -0
  38. package/dist/esm/data/meta.js +3 -0
  39. package/dist/esm/data/model.js +3 -0
  40. package/dist/esm/data/region.js +3 -0
  41. package/dist/esm/data/run.js +3 -0
  42. package/dist/esm/data/scenario.js +3 -0
  43. package/dist/esm/data/unit.js +3 -0
  44. package/dist/esm/dataframe/dataframe.js +1 -5
  45. package/dist/esm/index.js +1 -0
  46. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  47. package/dist/types/core/iamc/data.d.ts +33 -5
  48. package/dist/types/core/iamc/variable.d.ts +6 -0
  49. package/dist/types/core/meta.d.ts +6 -0
  50. package/dist/types/core/model.d.ts +6 -0
  51. package/dist/types/core/region.d.ts +6 -0
  52. package/dist/types/core/run.d.ts +6 -0
  53. package/dist/types/core/scenario.d.ts +6 -0
  54. package/dist/types/core/unit.d.ts +6 -0
  55. package/dist/types/core/utils.d.ts +30 -1
  56. package/dist/types/data/base.d.ts +4 -0
  57. package/dist/types/data/iamc/datapoint.d.ts +1 -0
  58. package/dist/types/data/iamc/variable.d.ts +1 -0
  59. package/dist/types/data/meta.d.ts +1 -0
  60. package/dist/types/data/model.d.ts +1 -0
  61. package/dist/types/data/region.d.ts +1 -0
  62. package/dist/types/data/run.d.ts +1 -0
  63. package/dist/types/data/scenario.d.ts +1 -0
  64. package/dist/types/data/unit.d.ts +1 -0
  65. package/dist/types/index.d.ts +2 -1
  66. package/dist/types/tsconfig.types.tsbuildinfo +1 -0
  67. package/package.json +4 -5
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InvalidCredentials = exports.BadFilterArguments = exports.InvalidRunMeta = exports.NoDefaultRunVersion = exports.SchemaError = exports.OperationNotSupported = exports.DeletionPrevented = exports.NotUnique = exports.NotFound = exports.Forbidden = exports.InvalidToken = exports.MissingToken = exports.PlatformNotUnique = exports.PlatformNotFound = exports.UnknownApiError = exports.ManagerApiError = exports.ImproperlyConfigured = exports.BadRequest = exports.InconsistentIamcType = exports.ProgrammingError = exports.IxmpError = exports.createError = void 0;
3
+ exports.InvalidCredentials = exports.BadFilterArguments = exports.InvalidRunMeta = exports.NoDefaultRunVersion = exports.SchemaError = exports.OperationNotSupported = exports.DeletionPrevented = exports.NotUnique = exports.NotFound = exports.Forbidden = exports.InvalidToken = exports.MissingToken = exports.PlatformNotUnique = exports.PlatformNotFound = exports.UnknownApiError = exports.ManagerApiError = exports.ImproperlyConfigured = exports.BadRequest = exports.InconsistentIamcType = exports.ProgrammingError = exports.IxmpError = void 0;
4
+ exports.createError = createError;
4
5
  class ProgrammingError extends Error {
5
6
  }
6
7
  exports.ProgrammingError = ProgrammingError;
@@ -210,7 +211,6 @@ function createError(options) {
210
211
  return new ImproperlyConfigured(Object.assign({ message: 'Could not find remote exception in registry. Are you sure client and server ixmp versions are compatible?' }, options));
211
212
  }
212
213
  }
213
- exports.createError = createError;
214
214
  // Register all error classes
215
215
  registerError(InconsistentIamcType);
216
216
  registerError(BadRequest);
@@ -23,6 +23,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.PlatformIamcData = exports.RunIamcData = void 0;
24
24
  const dataframe_1 = require("../../dataframe");
25
25
  const base_1 = require("../base");
26
+ const datapoint_1 = require("../../data/iamc/datapoint");
26
27
  const utils_1 = require("../utils");
27
28
  const variable_1 = require("./variable");
28
29
  /**
@@ -70,18 +71,12 @@ class RunIamcData extends base_1.BaseFacade {
70
71
  yield this.backend.iamc.datapoints.bulkDelete(df);
71
72
  });
72
73
  }
73
- /**
74
- * Tabulates IAMC format timeseries data with optional filtering.
75
- * @param filter Optional. Filter for tabulating IAMC format timeseries data.
76
- * @param wide Optional. Whether to return the data in IAMC wide format or not, defaults to false.
77
- * @returns A DataFrame representing the tabulated data.
78
- * @throws An error if illegal filters are applied.
79
- */
80
- tabulate(_a = {
81
- wide: false,
82
- }) {
83
- var { wide = false } = _a, filter = __rest(_a, ["wide"]);
84
- return __awaiter(this, void 0, void 0, function* () {
74
+ tabulate() {
75
+ return __awaiter(this, arguments, void 0, function* (_a = {
76
+ wide: false,
77
+ splitByType: false,
78
+ }) {
79
+ var { wide = false, splitByType = false } = _a, filter = __rest(_a, ["wide", "splitByType"]);
85
80
  if (filter !== undefined && filter !== null) {
86
81
  // these filters do not make sense when applied from a Run
87
82
  const illegalFilter = Object.keys(filter).filter((key) => {
@@ -91,7 +86,9 @@ class RunIamcData extends base_1.BaseFacade {
91
86
  throw new Error(`Illegal filter for 'iamc.tabulate()': ${illegalFilter}`);
92
87
  }
93
88
  }
94
- return yield this.repository.tabulate(Object.assign({ run: { id: this.run.id, defaultOnly: false }, joinRuns: false, wide }, filter));
89
+ const df = yield this.repository.tabulate(Object.assign(Object.assign({}, filter), { run: { id: this.run.id, defaultOnly: false }, joinRuns: false, wide,
90
+ splitByType }));
91
+ return df;
95
92
  });
96
93
  }
97
94
  contractParameters(df) {
@@ -135,19 +132,9 @@ class PlatformIamcData extends base_1.BaseFacade {
135
132
  super(backend);
136
133
  this.variables = new variable_1.VariableRepository(this.backend);
137
134
  }
138
- /**
139
- * Tabulates IAMC data with optional filtering.
140
- * @param filter Optional. Filter for retrieving IAMC data.
141
- * @param joinRuns Optional. Whether to join runs or not, defaults to true.
142
- * @param wide Optional. Whether to return the data in IAMC wide format or not, defaults to false.
143
- * @returns A Promise that resolves to a DataFrame containing the tabulated data.
144
- */
145
- tabulate(_a = {
146
- joinRuns: true,
147
- wide: false,
148
- }) {
149
- var { joinRuns = true, wide = false } = _a, filter = __rest(_a, ["joinRuns", "wide"]);
150
- return __awaiter(this, void 0, void 0, function* () {
135
+ tabulate() {
136
+ return __awaiter(this, arguments, void 0, function* (_a = { joinRuns: true, wide: false, splitByType: false }) {
137
+ var { joinRuns = true, wide = false, splitByType = false } = _a, filter = __rest(_a, ["joinRuns", "wide", "splitByType"]);
151
138
  if (filter === undefined) {
152
139
  filter = {};
153
140
  }
@@ -156,19 +143,54 @@ class PlatformIamcData extends base_1.BaseFacade {
156
143
  filter['run'] = { defaultOnly: true };
157
144
  }
158
145
  const joinParameters = true;
159
- let df = yield this.backend.iamc.datapoints.tabulate({
146
+ let df = (yield this.backend.iamc.datapoints.tabulate({
160
147
  joinRuns,
161
148
  joinParameters,
162
149
  filter,
163
- });
164
- // df = df.dropNa({ axis: 1 });
165
- if (df.columns.includes('time_series__id')) {
166
- df = df.dropColumns(['time_series__id']);
150
+ }));
151
+ if (splitByType) {
152
+ const splitDfs = (0, utils_1.splitDataFrameByType)(df);
153
+ const typeMapping = {
154
+ annual: datapoint_1.DataPointType.ANNUAL,
155
+ categorical: datapoint_1.DataPointType.CATEGORICAL,
156
+ datetime: datapoint_1.DataPointType.DATETIME,
157
+ };
158
+ const presentTypes = [];
159
+ Object.entries(typeMapping).forEach(([key, type]) => {
160
+ const typedKey = key;
161
+ if (splitDfs[typedKey]) {
162
+ presentTypes.push(type);
163
+ if (wide) {
164
+ splitDfs[typedKey] = (0, utils_1.toIamcWide)(splitDfs[typedKey]);
165
+ }
166
+ splitDfs[typedKey] = (0, utils_1.standardizeIamcDf)(splitDfs[typedKey]);
167
+ }
168
+ });
169
+ return Object.assign({ presentTypes }, splitDfs);
167
170
  }
168
- if (df.columns.includes('unit')) {
169
- df = df.replaceValue('dimensionless', ' ', 'unit');
171
+ else {
172
+ if (wide) {
173
+ df = (0, utils_1.toIamcWide)(df);
174
+ }
175
+ return (0, utils_1.standardizeIamcDf)(df);
176
+ }
177
+ });
178
+ }
179
+ /**
180
+ * Counts IAMC datapoints with optional filtering.
181
+ * @param filter Optional. Filter for counting IAMC datapoints.
182
+ * @returns A promise that resolves to the count of datapoints.
183
+ */
184
+ count(filter) {
185
+ return __awaiter(this, void 0, void 0, function* () {
186
+ if (filter === undefined) {
187
+ filter = {};
188
+ }
189
+ // return only default runs unless a run-filter is provided
190
+ if (!Object.hasOwn(filter, 'run')) {
191
+ filter['run'] = { defaultOnly: true };
170
192
  }
171
- return wide ? (0, utils_1.toIamcWide)(df) : df;
193
+ return this.backend.iamc.datapoints.count(filter);
172
194
  });
173
195
  }
174
196
  }
@@ -136,5 +136,15 @@ class VariableRepository extends base_1.BaseFacade {
136
136
  return this.backend.iamc.variables.tabulate(filter);
137
137
  });
138
138
  }
139
+ /**
140
+ * Counts Variables with optional filtering.
141
+ * @param filter Optional. Filter for counting Variables.
142
+ * @returns A Promise that resolves to the count of Variables.
143
+ */
144
+ count(filter) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ return this.backend.iamc.variables.count(filter);
147
+ });
148
+ }
139
149
  }
140
150
  exports.VariableRepository = VariableRepository;
@@ -33,17 +33,27 @@ class MetaIndicatorRepository extends base_1.BaseFacade {
33
33
  * @param joinRunIndex Optional. Whether to join the run index to the tabulated data, defaults to true.
34
34
  * @returns A promise that resolves to a DataFrame containing the tabulated meta indicators.
35
35
  */
36
- tabulate(_a = {
37
- joinRunIndex: true,
38
- }) {
39
- var { joinRunIndex = true } = _a, filter = __rest(_a, ["joinRunIndex"]);
40
- return __awaiter(this, void 0, void 0, function* () {
36
+ tabulate() {
37
+ return __awaiter(this, arguments, void 0, function* (_a = {
38
+ joinRunIndex: true,
39
+ }) {
40
+ var { joinRunIndex = true } = _a, filter = __rest(_a, ["joinRunIndex"]);
41
41
  if (filter === undefined) {
42
42
  filter = {};
43
43
  }
44
44
  return yield this.backend.meta.tabulate(filter, joinRunIndex);
45
45
  });
46
46
  }
47
+ /**
48
+ * Counts meta indicators with optional filtering.
49
+ * @param filter Optional. Filter for counting meta indicators.
50
+ * @returns A promise that resolves to the count of meta indicators.
51
+ */
52
+ count(filter) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ return this.backend.meta.count(filter || {});
55
+ });
56
+ }
47
57
  }
48
58
  exports.MetaIndicatorRepository = MetaIndicatorRepository;
49
59
  /**
@@ -137,5 +137,15 @@ class ModelRepository extends base_1.BaseFacade {
137
137
  return this.backend.models.tabulate((0, utils_1.withIamcDefault)(filter));
138
138
  });
139
139
  }
140
+ /**
141
+ * Counts models with optional filtering.
142
+ * @param filter Optional. Filter for counting models.
143
+ * @returns A promise that resolves to the count of models.
144
+ */
145
+ count(filter) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ return this.backend.models.count((0, utils_1.withIamcDefault)(filter));
148
+ });
149
+ }
140
150
  }
141
151
  exports.ModelRepository = ModelRepository;
@@ -168,5 +168,15 @@ class RegionRepository extends base_1.BaseFacade {
168
168
  return this.backend.regions.tabulate((0, utils_1.withIamcDefault)(filter));
169
169
  });
170
170
  }
171
+ /**
172
+ * Counts regions with optional filtering.
173
+ * @param filter Optional. Filter for counting regions.
174
+ * @returns A promise that resolves to the count of regions.
175
+ */
176
+ count(filter) {
177
+ return __awaiter(this, void 0, void 0, function* () {
178
+ return this.backend.regions.count((0, utils_1.withIamcDefault)(filter));
179
+ });
180
+ }
171
181
  }
172
182
  exports.RegionRepository = RegionRepository;
@@ -151,8 +151,8 @@ class RunRepository extends base_1.BaseFacade {
151
151
  * @param filter Optional. Filter for retriving runs.
152
152
  * @returns A promise that resolves to an array of Run instances.
153
153
  */
154
- list(filter = {}) {
155
- return __awaiter(this, void 0, void 0, function* () {
154
+ list() {
155
+ return __awaiter(this, arguments, void 0, function* (filter = {}) {
156
156
  if (filter.defaultOnly === undefined) {
157
157
  filter.defaultOnly = true;
158
158
  }
@@ -168,8 +168,8 @@ class RunRepository extends base_1.BaseFacade {
168
168
  * @param filter Optional. Filter for tabulating runs.
169
169
  * @returns A promise that resolves to a DataFrame containing the tabulated runs.
170
170
  */
171
- tabulate(filter = {}) {
172
- return __awaiter(this, void 0, void 0, function* () {
171
+ tabulate() {
172
+ return __awaiter(this, arguments, void 0, function* (filter = {}) {
173
173
  if (filter.defaultOnly === undefined) {
174
174
  filter.defaultOnly = true;
175
175
  }
@@ -190,5 +190,15 @@ class RunRepository extends base_1.BaseFacade {
190
190
  });
191
191
  });
192
192
  }
193
+ /**
194
+ * Counts runs with optional filtering.
195
+ * @param filter Optional. Filter for counting runs.
196
+ * @returns A promise that resolves to the count of runs.
197
+ */
198
+ count() {
199
+ return __awaiter(this, arguments, void 0, function* (filter = {}) {
200
+ return this.backend.runs.count((0, utils_1.withIamcDefault)(filter));
201
+ });
202
+ }
193
203
  }
194
204
  exports.RunRepository = RunRepository;
@@ -145,5 +145,15 @@ class ScenarioRepository extends base_1.BaseFacade {
145
145
  return this.backend.scenarios.tabulate((0, utils_1.withIamcDefault)(filter));
146
146
  });
147
147
  }
148
+ /**
149
+ * Counts scenarios with optional filtering.
150
+ * @param filter Optional. Filter for counting scenarios.
151
+ * @returns A promise that resolves to the count of scenarios.
152
+ */
153
+ count(filter) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ return this.backend.scenarios.count((0, utils_1.withIamcDefault)(filter));
156
+ });
157
+ }
148
158
  }
149
159
  exports.ScenarioRepository = ScenarioRepository;
@@ -182,5 +182,15 @@ class UnitRepository extends base_1.BaseFacade {
182
182
  return this.backend.units.tabulate((0, utils_1.withIamcDefault)(filter));
183
183
  });
184
184
  }
185
+ /**
186
+ * Counts units with optional filtering.
187
+ * @param filter Optional. Filter for counting units.
188
+ * @returns A promise that resolves to the count of units.
189
+ */
190
+ count(filter) {
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ return this.backend.units.count((0, utils_1.withIamcDefault)(filter));
193
+ });
194
+ }
185
195
  }
186
196
  exports.UnitRepository = UnitRepository;
@@ -1,6 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withIamcDefault = exports.toIamcWide = exports.dfToDimensionless = exports.unitToDimensionless = exports.substitudeType = void 0;
3
+ exports.substitudeType = substitudeType;
4
+ exports.unitToDimensionless = unitToDimensionless;
5
+ exports.dfToDimensionless = dfToDimensionless;
6
+ exports.standardizeIamcDf = standardizeIamcDf;
7
+ exports.toIamcWide = toIamcWide;
8
+ exports.withIamcDefault = withIamcDefault;
9
+ exports.getIamcRowIndex = getIamcRowIndex;
10
+ exports.sortTimesWithYearFirst = sortTimesWithYearFirst;
11
+ exports.splitDataFrameByType = splitDataFrameByType;
4
12
  const dataframe_1 = require("../dataframe");
5
13
  const datapoint_1 = require("../data/iamc/datapoint");
6
14
  function substitudeType(df, type) {
@@ -23,91 +31,144 @@ function substitudeType(df, type) {
23
31
  }
24
32
  return df;
25
33
  }
26
- exports.substitudeType = substitudeType;
27
34
  function unitToDimensionless(name) {
28
35
  if (name === 'dimensionless') {
29
36
  throw new Error("Unit name 'dimensionless' is reserved, use an empty string '' instead.");
30
37
  }
31
38
  return name === '' ? 'dimensionless' : name;
32
39
  }
33
- exports.unitToDimensionless = unitToDimensionless;
34
40
  function dfToDimensionless(df) {
35
41
  if (df
36
42
  .loc({ columns: ['unit'] })
37
- .values.map(toString)
43
+ .values.map(String)
38
44
  .includes('dimensionless')) {
39
45
  throw new Error("Unit name 'dimensionless' is reserved, use an empty string '' instead.");
40
46
  }
41
47
  df.replaceValue('', 'dimensionless', 'unit');
42
48
  return df;
43
49
  }
44
- exports.dfToDimensionless = dfToDimensionless;
50
+ /**
51
+ * Standardizes the columns of a DataFrame in IAMC format.
52
+ * @param df DataFrame in IAMC format
53
+ * @returns DataFrame in IAMC format with standardized columns
54
+ */
55
+ function standardizeIamcDf(df) {
56
+ if (df.columns.includes('time_series__id')) {
57
+ df = df.dropColumns(['time_series__id']);
58
+ }
59
+ if (df.columns.includes('unit')) {
60
+ df = df.replaceValue('dimensionless', ' ', 'unit');
61
+ }
62
+ // Rename step_category to subannual
63
+ if (df.columns.includes('step_category')) {
64
+ df = df.renameColumns({ step_category: 'subannual' });
65
+ }
66
+ return df;
67
+ }
68
+ /**
69
+ * Converts a DataFrame in IAMC long format to wide format.
70
+ * @param df The DataFrame to convert
71
+ * @returns A DataFrame in IAMC wide format
72
+ */
45
73
  function toIamcWide(df) {
46
- // cannot convert empty dataframe to wide format
74
+ // empty df: drop long-format columns if present
47
75
  if (df.shape[0] === 0) {
48
- // drop columns of iamc long format if present
49
- if (df.columns.includes('step_year')) {
50
- df = df.dropColumns(['step_year']);
51
- }
52
- if (df.columns.includes('value')) {
53
- df = df.dropColumns(['value']);
54
- }
55
- return df;
56
- }
57
- // TODO make this work for other types than ANNUAL as well
58
- const types = df.columnValues('type');
59
- if (types.some((type) => type !== 'ANNUAL')) {
60
- throw new Error('Only ANNUAL data is supported for conversion to wide format');
61
- }
62
- const allYears = [...new Set(df.columnValues('step_year'))];
63
- allYears.sort();
64
- const stepYearIndex = df.columns.indexOf('step_year');
65
- const valueIndex = df.columns.indexOf('value');
66
- const newColumnsIndices = df.columns
76
+ const longCols = [
77
+ 'step_year',
78
+ 'step_datetime',
79
+ 'step_category',
80
+ 'subannual',
81
+ 'value',
82
+ ];
83
+ const toDrop = longCols.filter((c) => df.columns.includes(c));
84
+ return toDrop.length ? df.dropColumns(toDrop) : df;
85
+ }
86
+ // column index map
87
+ const colIdx = Object.fromEntries(df.columns.map((c, i) => [c, i]));
88
+ // collect unique, non-null times
89
+ const uniqueNonNull = (arr) => [...new Set(arr.filter((v) => v !== null && v !== undefined))];
90
+ const years = df.columns.includes('step_year')
91
+ ? uniqueNonNull(df.columnValues('step_year')).sort((a, b) => a - b)
92
+ : [];
93
+ const datetimes = df.columns.includes('step_datetime')
94
+ ? uniqueNonNull(df.columnValues('step_datetime')).sort()
95
+ : [];
96
+ const allTimes = sortTimesWithYearFirst(years, datetimes);
97
+ // indices to carry over (everything but time and value)
98
+ const takeOverIdx = df.columns
67
99
  .map((_, i) => i)
68
- .filter((i) => i !== stepYearIndex && i !== valueIndex);
69
- const columns = [];
70
- const dtypes = [];
71
- for (const index of newColumnsIndices) {
72
- columns.push(df.columns[index]);
73
- dtypes.push(df.dtypes[index]);
74
- }
75
- const yearColumnsIndex = new Map();
76
- for (const year of allYears) {
77
- columns.push(year.toString());
100
+ .filter((i) => {
101
+ var _a, _b, _c;
102
+ return i !== ((_a = colIdx['step_year']) !== null && _a !== void 0 ? _a : -1) &&
103
+ i !== ((_b = colIdx['step_datetime']) !== null && _b !== void 0 ? _b : -1) &&
104
+ i !== ((_c = colIdx['value']) !== null && _c !== void 0 ? _c : -1);
105
+ });
106
+ // base schema (carry-over columns + dtypes)
107
+ const columns = takeOverIdx.map((i) => df.columns[i]);
108
+ const dtypes = takeOverIdx.map((i) => df.dtypes[i]);
109
+ // add time columns
110
+ const yearColIdx = new Map();
111
+ const dtColIdx = new Map();
112
+ for (const time of allTimes) {
113
+ columns.push(String(time));
78
114
  dtypes.push(dataframe_1.DType.FLOAT32);
79
- yearColumnsIndex.set(year, columns.length - 1);
115
+ const newIdx = columns.length - 1;
116
+ if (typeof time === 'number')
117
+ yearColIdx.set(time, newIdx);
118
+ else
119
+ dtColIdx.set(time, newIdx);
80
120
  }
81
- const rowIdentifiers = [
121
+ // row identifiers: prefer step_category, fall back to subannual if present
122
+ const stepCategoryCol = df.columns.includes('step_category')
123
+ ? 'step_category'
124
+ : df.columns.includes('subannual')
125
+ ? 'subannual'
126
+ : undefined;
127
+ const idCols = [
82
128
  'model',
83
129
  'scenario',
84
130
  'version',
85
131
  'region',
86
132
  'variable',
87
133
  'unit',
88
- ].map((col) => df.columns.indexOf(col));
134
+ 'type',
135
+ stepCategoryCol,
136
+ ].filter(Boolean);
137
+ const idIdxs = idCols.map((c) => colIdx[c]);
138
+ // data mapping
89
139
  const dataMap = new Map();
90
- for (const row of df.values) {
91
- const index = getIamcRowIndex(row, rowIdentifiers);
92
- if (dataMap.has(index)) {
93
- dataMap.get(index)[yearColumnsIndex.get(row[stepYearIndex])] =
94
- row[valueIndex];
140
+ const yearIdx = colIdx['step_year'];
141
+ const dtIdx = colIdx['step_datetime'];
142
+ const valueIdx = colIdx['value'];
143
+ for (const dfRow of df.values) {
144
+ const key = getIamcRowIndex(dfRow, idIdxs);
145
+ let wideRow = dataMap.get(key);
146
+ if (!wideRow) {
147
+ wideRow = takeOverIdx
148
+ .map((i) => dfRow[i])
149
+ .concat(Array(allTimes.length).fill(null));
150
+ dataMap.set(key, wideRow);
95
151
  }
96
- else {
97
- const wideRow = row
98
- .filter((_, i) => newColumnsIndices.includes(i))
99
- .concat(new Array(allYears.length).fill(null));
100
- wideRow[yearColumnsIndex.get(row[stepYearIndex])] = row[valueIndex];
101
- dataMap.set(index, wideRow);
152
+ const year = yearIdx !== undefined ? dfRow[yearIdx] : undefined;
153
+ const dt = dtIdx !== undefined ? dfRow[dtIdx] : undefined;
154
+ let targetIdx;
155
+ if (year !== undefined && year !== null)
156
+ targetIdx = yearColIdx.get(year);
157
+ else if (dt !== undefined && dt !== null)
158
+ targetIdx = dtColIdx.get(dt);
159
+ if (targetIdx !== undefined) {
160
+ wideRow[targetIdx] = dfRow[valueIdx];
102
161
  }
103
162
  }
104
- const wideIamcData = [];
105
- for (const wideRow of dataMap.values()) {
106
- wideIamcData.push(wideRow);
107
- }
163
+ const wideIamcData = Array.from(dataMap.values());
108
164
  return new dataframe_1.DataFrame(wideIamcData, { columns, dtypes });
109
165
  }
110
- exports.toIamcWide = toIamcWide;
166
+ /**
167
+ * Generates a unique row index for IAMC data based on the row's identifiers.
168
+ * @param row The row data as an array
169
+ * @param rowIdentifiers The indices of the identifiers in the row
170
+ * @returns A string that uniquely identifies the row
171
+ */
111
172
  function getIamcRowIndex(row, rowIdentifiers) {
112
173
  return rowIdentifiers.map((i) => row[i]).join('_');
113
174
  }
@@ -131,4 +192,75 @@ function withIamcDefault(filter) {
131
192
  }
132
193
  return filter;
133
194
  }
134
- exports.withIamcDefault = withIamcDefault;
195
+ function sortTimesWithYearFirst(years, dateTimes) {
196
+ // Create a combined array with type information
197
+ const combined = [];
198
+ // Add years with their metadata
199
+ for (const year of years) {
200
+ combined.push({ value: year, isYear: true, year });
201
+ }
202
+ // Add datetimes with their extracted year
203
+ for (const dateTime of dateTimes) {
204
+ // Extract year from datetime string (format: 2005-03-01T01:00:00)
205
+ const year = parseInt(dateTime.substring(0, 4), 10);
206
+ combined.push({ value: dateTime, isYear: false, year });
207
+ }
208
+ // Sort by year first, then by type (years before datetimes), then by value
209
+ combined.sort((a, b) => {
210
+ // First sort by year
211
+ if (a.year !== b.year) {
212
+ return a.year - b.year;
213
+ }
214
+ // Within the same year, years come before datetimes
215
+ if (a.isYear && !b.isYear) {
216
+ return -1;
217
+ }
218
+ if (!a.isYear && b.isYear) {
219
+ return 1;
220
+ }
221
+ // If both are the same type, sort by value
222
+ if (a.value < b.value) {
223
+ return -1;
224
+ }
225
+ if (a.value > b.value) {
226
+ return 1;
227
+ }
228
+ return 0;
229
+ });
230
+ // Return just the values
231
+ return combined.map((item) => item.value);
232
+ }
233
+ /**
234
+ * Splits a DataFrame into separate DataFrames based on DataPointType.
235
+ * @param df The DataFrame to split
236
+ * @returns An object containing DataFrames for each type present in the data
237
+ */
238
+ function splitDataFrameByType(df) {
239
+ const result = {};
240
+ // If no type column exists, return the original dataframe as annual
241
+ if (!df.columns.includes('type')) {
242
+ result.annual = df;
243
+ return result;
244
+ }
245
+ // Get unique types in the dataframe
246
+ const typeValues = df.columnValues('type');
247
+ const uniqueTypes = [...new Set(typeValues)];
248
+ // Filter dataframe for each type present
249
+ for (const type of uniqueTypes) {
250
+ const filteredDf = df.query({
251
+ conditions: { column: 'type', predicate: (value) => value === type },
252
+ });
253
+ switch (type) {
254
+ case datapoint_1.DataPointType.ANNUAL:
255
+ result.annual = filteredDf;
256
+ break;
257
+ case datapoint_1.DataPointType.CATEGORICAL:
258
+ result.categorical = filteredDf;
259
+ break;
260
+ case datapoint_1.DataPointType.DATETIME:
261
+ result.datetime = filteredDf;
262
+ break;
263
+ }
264
+ }
265
+ return result;
266
+ }
@@ -26,8 +26,8 @@ class BaseRepository {
26
26
  this.enumerationMethod = (_a = this.constructor.enumerationMethod) !== null && _a !== void 0 ? _a : 'PATCH';
27
27
  }
28
28
  _request(url, method, params, body) {
29
- var _a;
30
29
  return __awaiter(this, void 0, void 0, function* () {
30
+ var _a;
31
31
  const config = {};
32
32
  config.url = url;
33
33
  config.method = method;
@@ -80,8 +80,8 @@ class BaseRepository {
80
80
  /**
81
81
  * Convenience method for requests to the enumeration endpoint.
82
82
  */
83
- _requestEnumeration(params = {}, body = {}, table = false) {
84
- return __awaiter(this, void 0, void 0, function* () {
83
+ _requestEnumeration() {
84
+ return __awaiter(this, arguments, void 0, function* (params = {}, body = {}, table = false) {
85
85
  params = (0, utils_1.convertToSnakeCase)(params);
86
86
  if (body !== undefined && Object.keys(body).length === 0) {
87
87
  body = undefined;
@@ -116,8 +116,8 @@ class BaseRepository {
116
116
  }
117
117
  });
118
118
  }
119
- _list({ filter, params, }) {
120
- return __awaiter(this, void 0, void 0, function* () {
119
+ _list(_a) {
120
+ return __awaiter(this, arguments, void 0, function* ({ filter, params, }) {
121
121
  if (this.enumerationMethod === 'GET') {
122
122
  params = Object.assign(Object.assign({}, params), filter);
123
123
  filter = undefined;
@@ -133,8 +133,8 @@ class BaseRepository {
133
133
  }
134
134
  });
135
135
  }
136
- _tabulate({ filter, params, }) {
137
- return __awaiter(this, void 0, void 0, function* () {
136
+ _tabulate(_a) {
137
+ return __awaiter(this, arguments, void 0, function* ({ filter, params, }) {
138
138
  // TODO: test what happens if we get a number that cannot be represented as float32 (or int32)
139
139
  const data = yield this._requestEnumeration(params, filter, true);
140
140
  const pagination = data.pagination;
@@ -156,6 +156,24 @@ class BaseRepository {
156
156
  }
157
157
  });
158
158
  }
159
+ _count(_a) {
160
+ return __awaiter(this, arguments, void 0, function* ({ filter, params, }) {
161
+ if (this.enumerationMethod === 'GET') {
162
+ params = Object.assign(Object.assign(Object.assign({}, params), filter), { limit: 0 });
163
+ filter = undefined;
164
+ }
165
+ else {
166
+ params = Object.assign(Object.assign({}, params), { limit: 0 });
167
+ }
168
+ const data = yield this._requestEnumeration(params, filter, true);
169
+ if (data === undefined || data.total === undefined) {
170
+ throw new exceptions_1.UnknownApiError({
171
+ message: 'Count result does not contain total count.',
172
+ });
173
+ }
174
+ return data.total;
175
+ });
176
+ }
159
177
  _bulkUpsert(df, params) {
160
178
  return __awaiter(this, void 0, void 0, function* () {
161
179
  return yield this._request(`${this.prefix}bulk/`, 'POST', params, (0, utils_1.dfToJson)(df));