SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
sddskde.c
Go to the documentation of this file.
1/**
2 * @file sddskde.c
3 * @brief Kernel Density Estimation for SDDS Data.
4 *
5 * @details
6 * This program performs Kernel Density Estimation (KDE) for one-dimensional data
7 * read from an SDDS file. It computes probability density functions (PDFs) and cumulative
8 * distribution functions (CDFs) for the specified columns and writes the results to
9 * an SDDS output file. It uses Gaussian kernel functions for KDE calculations and supports
10 * options for selecting columns and customizing the margin for data extension.
11 *
12 * @section Usage
13 * ```
14 * sddskde [<inputfile>] [<outputfile>]
15 * [-pipe=[input][,output]]
16 * -column=<list of columns>
17 * [-margin=<value>]
18 * [-threads=<number>]
19 * ```
20 *
21 * @section Options
22 * | Required | Description |
23 * |---------------------|---------------------------------------------------------------------------------------|
24 * | `-column` | Specifies the columns for KDE calculations. Comma-separated and supports wildcards. |
25 *
26 * | Optional | Description |
27 * |---------------------|-----------------------------------------------------------------|
28 * | `-pipe` | Enable SDDS Toolkit piping for input/output. |
29 * | `-margin` | Set the margin as a ratio to extend data range (default 0.3). |
30 * | `-threads` | Set the number of threads for PDF sample evaluation. |
31 *
32 * @copyright
33 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
34 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
35 *
36 * @license
37 * This file is distributed under the terms of the Software License Agreement
38 * found in the file LICENSE included with this distribution.
39 *
40 * @authors
41 * Yipeng Sun, H. Shang, M. Borland, R. Soliday
42 */
43
44#include <math.h>
45#include <gsl/gsl_statistics.h>
46#include <gsl/gsl_sort.h>
47#include <gsl/gsl_math.h>
48#include "SDDS.h"
49#include "mdb.h"
50#include "scan.h"
51#include "SDDSutils.h"
52
53double bandwidth(double data[], int64_t M);
54double gaussiankernelfunction(double sample);
55double kerneldensityestimate(double *trainingdata, double sample, int64_t n, double h);
56double *linearspace(double initial, double final, int N);
57
58/* Enumeration for option types */
59enum option_type {
60 SET_COLUMN,
61 SET_PIPE,
62 SET_MARGIN,
63 SET_THREADS,
64 N_OPTIONS
65};
66
67static char *option[N_OPTIONS] = {
68 "column",
69 "pipe",
70 "margin",
71 "threads",
72};
73
74static const char *usage =
75 "sddskde [<inputfile>] [<outputfile>]\n"
76 " [-pipe=[input][,output]]\n"
77 " -column=<list of columns>\n"
78 " [-margin=<value>]\n"
79 " [-threads=<number>]\n"
80 "Options:\n"
81 "-column provide column names separated by commas, wild card accepted.\n"
82 "-margin provide the ratio to extend the original data, default 0.3.\n"
83 "-threads number of threads to use for PDF sample evaluation.\n"
84 "-pipe The standard SDDS Toolkit pipe option.\n\n"
85 "sddskde performs kernel density estimation for one-dimensional data.\n"
86 "Program by Yipeng Sun and Hairong Shang (" __DATE__ " " __TIME__ ", SVN revision: " SVN_VERSION ").\n";
87
88int main(int argc, char **argv) {
89 int n_test = 100;
90 double min_temp, max_temp;
91 double bwidth, gap, h, lower, upper;
92 double margin = 0.3;
93 SDDS_DATASET sdds_in, sdds_out;
94 char *input_file = NULL, *output_file = NULL, **column = NULL;
95 long tmpfile_used = 0, columns = 0, no_warnings = 1;
96 unsigned long pipe_flags;
97 long i, i_arg, j;
98 SCANNED_ARG *s_arg;
99 double *column_data = NULL, *pdf = NULL, *cdf = NULL;
100 char buffer_pdf[1024], buffer_cdf[1024], buffer_pdf_units[1024];
101 int64_t rows;
102 int threads = 1;
103
105 argc = scanargs(&s_arg, argc, argv);
106 pipe_flags = 0;
107
108 if (argc < 2) {
109 fprintf(stderr, "%s", usage);
110 exit(EXIT_FAILURE);
111 }
112
113 for (i_arg = 1; i_arg < argc; i_arg++) {
114 if (s_arg[i_arg].arg_type == OPTION) {
115 delete_chars(s_arg[i_arg].list[0], "_");
116 switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
117 case SET_COLUMN:
118 columns = s_arg[i_arg].n_items - 1;
119 column = realloc(column, sizeof(*column) * columns);
120 for (i = 1; i < s_arg[i_arg].n_items; i++) {
121 column[i - 1] = s_arg[i_arg].list[i];
122 }
123 break;
124 case SET_MARGIN:
125 if (s_arg[i_arg].n_items != 2) {
126 SDDS_Bomb("Invalid -margin option!");
127 }
128 if (!get_double(&margin, s_arg[i_arg].list[1])) {
129 SDDS_Bomb("Invalid -margin value provided!");
130 }
131 break;
132 case SET_THREADS:
133 if (s_arg[i_arg].n_items != 2 ||
134 sscanf(s_arg[i_arg].list[1], "%d", &threads) != 1 || threads < 1) {
135 SDDS_Bomb("invalid -threads syntax");
136 }
137 break;
138 case SET_PIPE:
139 if (!processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipe_flags)) {
140 fprintf(stderr, "Error (%s): invalid -pipe syntax\n", argv[0]);
141 return EXIT_FAILURE;
142 }
143 break;
144 default:
145 fprintf(stderr, "Unknown option: %s\n", s_arg[i_arg].list[0]);
146 exit(EXIT_FAILURE);
147 }
148 } else {
149 if (!input_file) {
150 input_file = s_arg[i_arg].list[0];
151 } else if (!output_file) {
152 output_file = s_arg[i_arg].list[0];
153 } else {
154 fprintf(stderr, "Error (%s): too many filenames\n", argv[0]);
155 return EXIT_FAILURE;
156 }
157 }
158 }
159
160 processFilenames("sddskde", &input_file, &output_file, pipe_flags, no_warnings, &tmpfile_used);
161
162 if (!columns) {
163 fprintf(stderr, "%s", usage);
164 SDDS_Bomb("No column provided!");
165 }
166
167 if (!SDDS_InitializeInput(&sdds_in, input_file)) {
168 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
169 return EXIT_FAILURE;
170 }
171
172 if ((columns = expandColumnPairNames(&sdds_in, &column, NULL, columns, NULL, 0, FIND_NUMERIC_TYPE, 0)) <= 0) {
173 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
174 SDDS_Bomb("No columns selected.");
175 }
176
177 if (!SDDS_InitializeOutput(&sdds_out, SDDS_BINARY, 1, NULL, NULL, output_file)) {
178 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
179 }
180
181 for (i = 0; i < columns; i++) {
182 char *units = NULL;
183 if (SDDS_GetColumnInformation(&sdds_in, "units", &units, SDDS_GET_BY_NAME, column[i]) != SDDS_STRING) {
184 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
185 }
186
187 if (units && strlen(units)) {
188 snprintf(buffer_pdf_units, sizeof(buffer_pdf_units), "1/(%s)", units);
189 } else {
190 buffer_pdf_units[0] = '\0';
191 }
192
193 free(units);
194
195 snprintf(buffer_pdf, sizeof(buffer_pdf), "%sPDF", column[i]);
196 snprintf(buffer_cdf, sizeof(buffer_cdf), "%sCDF", column[i]);
197
198 if (!SDDS_TransferColumnDefinition(&sdds_out, &sdds_in, column[i], NULL) ||
199 SDDS_DefineColumn(&sdds_out, buffer_pdf, NULL, buffer_pdf_units, NULL, NULL, SDDS_DOUBLE, 0) < 0 ||
200 SDDS_DefineColumn(&sdds_out, buffer_cdf, NULL, NULL, NULL, NULL, SDDS_DOUBLE, 0) < 0) {
201 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
202 }
203 }
204
205 if (!SDDS_WriteLayout(&sdds_out)) {
206 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
207 }
208
209 pdf = malloc(sizeof(*pdf) * n_test);
210 cdf = malloc(sizeof(*cdf) * n_test);
211
212 while (SDDS_ReadPage(&sdds_in) >= 0) {
213 if ((rows = SDDS_CountRowsOfInterest(&sdds_in))) {
214 if (!SDDS_StartPage(&sdds_out, n_test)) {
215 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
216 }
217
218 for (i = 0; i < columns; i++) {
219 snprintf(buffer_pdf, sizeof(buffer_pdf), "%sPDF", column[i]);
220 snprintf(buffer_cdf, sizeof(buffer_cdf), "%sCDF", column[i]);
221
222 if (!(column_data = SDDS_GetColumnInDoubles(&sdds_in, column[i]))) {
223 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
224 }
225
226 find_min_max(&min_temp, &max_temp, column_data, rows);
227 gap = max_temp - min_temp;
228 lower = min_temp - gap * margin;
229 upper = max_temp + gap * margin;
230
231 double *x_array = linearspace(lower, upper, n_test);
232 bwidth = bandwidth(column_data, rows);
233 h = GSL_MAX(bwidth, 2e-6);
234
235#pragma omp parallel for if (threads > 1) num_threads(threads)
236 for (j = 0; j < n_test; j++) {
237 pdf[j] = kerneldensityestimate(column_data, x_array[j], rows, h);
238 cdf[j] = pdf[j];
239 }
240
241 for (j = 1; j < n_test; j++) {
242 cdf[j] += cdf[j - 1];
243 }
244
245 for (j = 0; j < n_test; j++) {
246 cdf[j] = cdf[j] / cdf[n_test - 1];
247 }
248
249 if (!SDDS_SetColumnFromDoubles(&sdds_out, SDDS_SET_BY_NAME, pdf, n_test, buffer_pdf) ||
250 !SDDS_SetColumnFromDoubles(&sdds_out, SDDS_SET_BY_NAME, cdf, n_test, buffer_cdf) ||
251 !SDDS_SetColumnFromDoubles(&sdds_out, SDDS_SET_BY_NAME, x_array, n_test, column[i])) {
252 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
253 }
254
255 free(column_data);
256 column_data = NULL;
257
258 free(x_array);
259 x_array = NULL;
260 }
261 }
262
263 if (!SDDS_WritePage(&sdds_out)) {
264 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
265 }
266 }
267
268 free(pdf);
269 free(cdf);
270
271 if (!SDDS_Terminate(&sdds_in) || !SDDS_Terminate(&sdds_out)) {
272 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
273 }
274
275 if (tmpfile_used && !replaceFileAndBackUp(input_file, output_file)) {
276 return EXIT_FAILURE;
277 }
278
279 free(column);
280
281 return EXIT_SUCCESS;
282}
283
284double bandwidth(double data[], int64_t M) {
285 double sigma, min_val, bwidth, interquartile_range;
286 double hspread_three, hspread_one;
287
288 gsl_sort(data, 1, M);
289 sigma = gsl_stats_sd(data, 1, M);
290 hspread_three = gsl_stats_quantile_from_sorted_data(data, 1, M, 0.750);
291 hspread_one = gsl_stats_quantile_from_sorted_data(data, 1, M, 0.250);
292 interquartile_range = hspread_three - hspread_one;
293 min_val = GSL_MIN(interquartile_range / 1.339999, sigma);
294 bwidth = 0.90 * min_val * pow(M, -0.20);
295
296 return bwidth;
297}
298
299double gaussiankernelfunction(double sample) {
300 double k;
301 k = exp(-(gsl_pow_2(sample) / 2.0));
302 k = k / (M_SQRT2 * sqrt(M_PI));
303
304 return k;
305}
306
307double kerneldensityestimate(double *trainingdata, double sample, int64_t n, double h) {
308 int64_t i;
309 double pdf = 0.0;
310
311 for (i = 0; i < n; i++) {
312 pdf += gaussiankernelfunction((trainingdata[i] - sample) / h);
313 }
314
315 pdf = pdf / (n * h);
316
317 return pdf;
318}
319
320double *linearspace(double start, double end, int N) {
321 double *x;
322 int i;
323 double step;
324
325 x = calloc(N, sizeof(double));
326 step = (end - start) / (double)(N - 1);
327
328 x[0] = start;
329 for (i = 1; i < N; i++) {
330 x[i] = x[i - 1] + step;
331 }
332 x[N - 1] = end;
333
334 return x;
335}
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_GetColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Retrieves information about a specified column in the SDDS dataset.
Definition SDDS_info.c:41
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
#define SDDS_STRING
Identifier for the string data type.
Definition SDDStypes.h:85
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
Utility functions for SDDS dataset manipulation and string array operations.
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