SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
sddskde2d.c
Go to the documentation of this file.
1/**
2 * @file sddskde2d.c
3 * @brief Performs kernel density estimation (KDE) for two-dimensional data using the SDDS library.
4 *
5 * @details
6 * This program processes input data from an SDDS file, applies kernel density estimation
7 * to two selected columns, and outputs the resulting density estimates to a new SDDS file.
8 * It supports flexible options such as specifying columns, margins, and whether
9 * to use the same scales across data pages.
10 *
11 * @section Usage
12 * ```
13 * sddskde2d [<inputfile>] [<outputfile>]
14 * [-pipe=[input][,output]]
15 * -column=<column1,column2>
16 * [-samescales]
17 * [-margin=<value>]
18 * [-threads=<number>]
19 * ```
20 *
21 * @section Options
22 * | Required | Description |
23 * |---------------------|---------------------------------------------------------------------------------------|
24 * | `-column` | Specify two column names for KDE, separated by a comma. Wildcards accepted. |
25 *
26 * | Option | Description |
27 * |------------------------|-------------------------------------------------------------------------|
28 * | `-pipe` | Utilize the standard SDDS Toolkit pipe option. |
29 * | `-samescales` | Use the same X and Y ranges for all output pages. |
30 * | `-margin` | Ratio to extend the original data range (default: 0.05). |
31 * | `-threads` | Set the number of threads for grid PDF evaluation. |
32 *
33 * ### Features
34 * - Reads input data from SDDS files and writes results in SDDS format.
35 * - Allows users to specify the columns for KDE using wildcards.
36 * - Handles margins and ensures proper scaling of the data.
37 * - Supports generating grids for density estimation.
38 * - Implements bandwidth selection and Gaussian kernel functions.
39 *
40 * @copyright
41 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
42 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
43 *
44 * @license
45 * This file is distributed under the terms of the Software License Agreement
46 * found in the file LICENSE included with this distribution.
47 *
48 * @author
49 * Yipeng Sun, M. Borland, R. Soliday
50 */
51
52#include <math.h>
53#include <gsl/gsl_statistics.h>
54#include <gsl/gsl_sort.h>
55#include <gsl/gsl_math.h>
56#include "SDDS.h"
57#include "mdb.h"
58#include "scan.h"
59#include "SDDSutils.h"
60
61double bandwidth(double data[], int64_t M);
62double gaussiankernelfunction(double sample);
63double kerneldensityestimate(double *trainingdata_x, double *trainingdata_y,
64 double sample_x, double sample_y, int64_t n,
65 double hx, double hy);
66double *gridX(double start, double end, int N);
67double *gridY(double start, double end, int N);
68
69/* Enumeration for option types */
70enum option_type {
71 SET_COLUMN,
72 SET_PIPE,
73 SET_MARGIN,
74 SET_SAME_SCALES,
75 SET_THREADS,
76 N_OPTIONS
77};
78
79char *option[N_OPTIONS] = {
80 "column",
81 "pipe",
82 "margin",
83 "samescales",
84 "threads"};
85
86static char *usage =
87 "Usage: sddskde2d [<inputfile>] [<outputfile>] \n"
88 " [-pipe=[input][,output]] \n"
89 " -column=<column1,column2> \n"
90 " [-samescales] \n"
91 " [-margin=<value>]\n"
92 " [-threads=<number>]\n\n"
93 "Options:\n"
94 " -column Specify two column names separated by a comma. Wildcards are accepted.\n"
95 " -margin Ratio to extend the original data (default: 0.05).\n"
96 " -samescales Use the same X and Y ranges for all output pages.\n"
97 " -threads Number of threads to use for grid PDF evaluation.\n"
98 " -pipe Utilize the standard SDDS Toolkit pipe option.\n\n"
99 "Description:\n"
100 " sddskde2d performs kernel density estimation for two-dimensional data.\n\n"
101 "Author:\n"
102 " Yipeng Sun (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ")\n";
103
104int main(int argc, char **argv) {
105 int n_test, n_total, same_scales, threads;
106 double lowerx, upperx;
107 double lowery, uppery;
108 double margin = 0.05;
109 SDDS_DATASET SDDSin, SDDSout;
110 char *input_file, *output_file, **column;
111 long tmpfile_used = 0, columns, no_warnings = 1;
112 unsigned long pipe_flags;
113 long i, i_arg, j;
114 SCANNED_ARG *s_arg;
115 double **column_data_x, **column_data_y, *pdf;
116 int64_t *rows, rows0;
117 int32_t n_pages, i_page;
118
119 input_file = output_file = NULL;
120 pdf = NULL;
121 columns = 0;
122 column = NULL;
123 same_scales = 0;
124 threads = 1;
125
126 n_test = 50;
127 n_total = n_test * n_test;
128
130 argc = scanargs(&s_arg, argc, argv);
131 pipe_flags = 0;
132 tmpfile_used = 0;
133 if (argc < 2) {
134 fprintf(stderr, "%s", usage);
135 exit(1);
136 }
137 for (i_arg = 1; i_arg < argc; i_arg++) {
138 if (s_arg[i_arg].arg_type == OPTION) {
139 delete_chars(s_arg[i_arg].list[0], "_");
140 switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
141 case SET_COLUMN:
142 columns = s_arg[i_arg].n_items - 1;
143 column = trealloc(column, sizeof(*column) * columns);
144 for (i = 1; i < s_arg[i_arg].n_items; i++)
145 column[i - 1] = s_arg[i_arg].list[i];
146 break;
147 case SET_MARGIN:
148 if (s_arg[i_arg].n_items != 2)
149 SDDS_Bomb("Invalid -margin option. Too many qualifiers.");
150 if (!get_double(&margin, s_arg[i_arg].list[1]))
151 SDDS_Bomb("Invalid -margin value provided.");
152 break;
153 case SET_SAME_SCALES:
154 if (s_arg[i_arg].n_items != 1)
155 SDDS_Bomb("Invalid -sameScales option. No qualifiers are accepted.");
156 same_scales = 1;
157 break;
158 case SET_THREADS:
159 if (s_arg[i_arg].n_items != 2 ||
160 sscanf(s_arg[i_arg].list[1], "%d", &threads) != 1 || threads < 1)
161 SDDS_Bomb("invalid -threads syntax");
162 break;
163 case SET_PIPE:
164 if (!processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipe_flags)) {
165 fprintf(stderr, "Error (%s): invalid -pipe syntax\n", argv[0]);
166 return 1;
167 }
168 break;
169 }
170 } else {
171 if (!input_file)
172 input_file = s_arg[i_arg].list[0];
173 else if (output_file == NULL)
174 output_file = s_arg[i_arg].list[0];
175 else {
176 fprintf(stderr, "Error (%s): too many filenames\n", argv[0]);
177 return 1;
178 }
179 }
180 }
181 processFilenames("sddskde2d", &input_file, &output_file, pipe_flags, no_warnings, &tmpfile_used);
182 if (!columns) {
183 fprintf(stderr, "%s", usage);
184 SDDS_Bomb("No column provided!");
185 }
186 if (!SDDS_InitializeInput(&SDDSin, input_file)) {
187 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
188 return 1;
189 }
190 if ((columns = expandColumnPairNames(&SDDSin, &column, NULL, columns, NULL, 0, FIND_NUMERIC_TYPE, 0)) <= 0) {
191 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
192 SDDS_Bomb("no columns selected.");
193 }
194 if (columns > 2) {
195 fprintf(stderr, "%s", usage);
196 SDDS_Bomb("Only 2 columns may be accepted.");
197 }
198 if (!SDDS_InitializeOutput(&SDDSout, SDDS_BINARY, 1, NULL, NULL, output_file))
199 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
200 if (!SDDS_TransferColumnDefinition(&SDDSout, &SDDSin, column[0], NULL) ||
201 !SDDS_TransferColumnDefinition(&SDDSout, &SDDSin, column[1], NULL) ||
202 SDDS_DefineColumn(&SDDSout, "PDF", NULL, NULL, NULL, NULL, SDDS_DOUBLE, 0) < 0)
203 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
204 if (!SDDS_WriteLayout(&SDDSout))
205 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
206
207 /* Read all the data in case we need to use fixed scales */
208 n_pages = 0;
209 column_data_x = NULL;
210 column_data_y = NULL;
211 rows = NULL;
212 while (SDDS_ReadPage(&SDDSin) >= 0) {
213 if ((rows0 = SDDS_CountRowsOfInterest(&SDDSin))) {
214 column_data_x = (double **)SDDS_Realloc(column_data_x, sizeof(*column_data_x) * (n_pages + 1));
215 column_data_y = (double **)SDDS_Realloc(column_data_y, sizeof(*column_data_y) * (n_pages + 1));
216 rows = (int64_t *)SDDS_Realloc(rows, sizeof(*rows) * (n_pages + 1));
217 rows[n_pages] = rows0;
218 if (!(column_data_x[n_pages] = SDDS_GetColumnInDoubles(&SDDSin, column[0])))
219 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
220 if (!(column_data_y[n_pages] = SDDS_GetColumnInDoubles(&SDDSin, column[1])))
221 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
222 n_pages++;
223 }
224 }
225 if (n_pages == 0)
226 SDDS_Bomb("No data in file");
227
228 if (same_scales) {
229 double llx, lly, uux, uuy;
230 lowerx = lowery = DBL_MAX;
231 upperx = uppery = -DBL_MAX;
232 for (i_page = 0; i_page < n_pages; i_page++) {
233 find_min_max(&llx, &uux, column_data_x[i_page], rows[i_page]);
234 lowerx = MIN(llx, lowerx);
235 upperx = MAX(uux, upperx);
236 find_min_max(&lly, &uuy, column_data_y[i_page], rows[i_page]);
237 lowery = MIN(lly, lowery);
238 uppery = MAX(uuy, uppery);
239 }
240 }
241
242 pdf = malloc(sizeof(*pdf) * n_total);
243 for (i_page = 0; i_page < n_pages; i_page++) {
244 if (!SDDS_StartPage(&SDDSout, n_total))
245 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
246 if (!same_scales) {
247 find_min_max(&lowerx, &upperx, column_data_x[i_page], rows[i_page]);
248 find_min_max(&lowery, &uppery, column_data_y[i_page], rows[i_page]);
249 }
250 double *x_array = gridX(lowerx, upperx, n_test);
251 double *y_array = gridY(lowery, uppery, n_test);
252 double hx = bandwidth(column_data_x[i_page], rows[i_page]);
253 double hy = bandwidth(column_data_y[i_page], rows[i_page]);
254#pragma omp parallel for if (threads > 1) num_threads(threads)
255 for (j = 0; j < n_total; j++) {
256 pdf[j] = kerneldensityestimate(column_data_x[i_page], column_data_y[i_page], x_array[j], y_array[j], rows[i_page], hx, hy);
257 }
258 if (!SDDS_SetColumnFromDoubles(&SDDSout, SDDS_SET_BY_NAME, x_array, n_total, column[0]))
259 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
260 if (!SDDS_SetColumnFromDoubles(&SDDSout, SDDS_SET_BY_NAME, y_array, n_total, column[1]))
261 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
262 if (!SDDS_SetColumnFromDoubles(&SDDSout, SDDS_SET_BY_NAME, pdf, n_total, "PDF"))
263 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
264 free(column_data_x[i_page]);
265 free(column_data_y[i_page]);
266 free(x_array);
267 free(y_array);
268 if (!SDDS_WritePage(&SDDSout))
269 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
270 }
271 free(pdf);
272 free(column_data_x);
273 free(column_data_y);
274 free(rows);
275 if (!SDDS_Terminate(&SDDSin) || !SDDS_Terminate(&SDDSout))
276 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
277 if (tmpfile_used && !replaceFileAndBackUp(input_file, output_file)) {
278 return 1;
279 }
280 free(column);
281 return 0;
282}
283
284double bandwidth(double data[], int64_t M) {
285 double sigma, bwidth, silver_factor;
286 sigma = gsl_stats_sd(data, 1, M);
287 silver_factor = pow(M, -0.16666666);
288 bwidth = pow(silver_factor, 2.0) * pow(sigma, 2.0);
289 return bwidth;
290}
291
292double gaussiankernelfunction(double sample) {
293 double k;
294 k = exp(-sample / 2.0);
295 k = k / (2.0 * M_PI);
296 return k;
297}
298
299double kerneldensityestimate(double *trainingdata_x, double *trainingdata_y,
300 double sample_x, double sample_y, int64_t n,
301 double hx, double hy) {
302 int64_t i;
303 double pdf, z;
304
305 pdf = 0.0;
306 for (i = 0; i < n; i++) {
307 z = (trainingdata_x[i] - sample_x) * (trainingdata_x[i] - sample_x) / hx;
308 z += (trainingdata_y[i] - sample_y) * (trainingdata_y[i] - sample_y) / hy;
309 pdf += gaussiankernelfunction(z);
310 }
311 pdf = pdf / (n * sqrt(hx) * sqrt(hy));
312 return pdf;
313}
314
315double *gridX(double start, double end, int N) {
316 double *x;
317 int i, j, n_grid;
318 double step;
319
320 n_grid = N * N;
321 x = (double *)calloc(n_grid, sizeof(double));
322 step = (end - start) / (double)(N - 1);
323
324 i = 0;
325 for (j = 0; j < N; j++) {
326 for (i = 0; i < N; i++) {
327 x[i + j * N] = start + i * step;
328 }
329 }
330 return x;
331}
332
333double *gridY(double start, double end, int N) {
334 double *x;
335 int i, j, n_grid;
336 double step;
337
338 n_grid = N * N;
339 x = (double *)calloc(n_grid, sizeof(double));
340 step = (end - start) / (double)(N - 1);
341
342 i = 0;
343 for (j = 0; j < N; j++) {
344 for (i = 0; i < N; i++) {
345 x[i + j * N] = start + j * step;
346 }
347 }
348 return x;
349}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetColumnFromDoubles(SDDS_DATASET *SDDS_dataset, int32_t mode, double *data, int64_t rows,...)
Sets the values for a single data column using double-precision floating-point numbers.
int64_t SDDS_CountRowsOfInterest(SDDS_DATASET *SDDS_dataset)
Counts the number of rows marked as "of interest" in the current data table.
double * SDDS_GetColumnInDoubles(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves the data of a specified numerical column as an array of doubles, considering only rows mark...
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:50
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_ReadPage(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_DefineColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length)
Defines a data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_TransferColumnDefinition(SDDS_DATASET *target, SDDS_DATASET *source, char *name, char *newName)
Transfers a column definition from a source dataset to a target dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:474
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:318
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
Definition SDDS_utils.c:380
void * SDDS_Realloc(void *old_ptr, size_t new_size)
Reallocates memory to a new size.
Definition SDDS_utils.c:743
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
Utility functions for SDDS dataset manipulation and string array operations.
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
Definition array.c:190
int get_double(double *dptr, char *s)
Parses a double value from the given string.
Definition data_scan.c:40
char * delete_chars(char *s, char *t)
Removes all occurrences of characters found in string t from string s.
int find_min_max(double *min, double *max, double *list, int64_t n)
Finds the minimum and maximum values in a list of doubles.
Definition findMinMax.c:33
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
long replaceFileAndBackUp(char *file, char *replacement)
Replaces a file with a replacement file and creates a backup of the original.
Definition replacefile.c:78
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
long processPipeOption(char **item, long items, unsigned long *flags)
Definition scanargs.c:357
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
Definition scanargs.c:391